Add multi-module release launcher example
This commit is contained in:
55
examples/multi-module/README.md
Normal file
55
examples/multi-module/README.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Multi-module release example
|
||||
|
||||
This example maps selectors and complete event tags to a physical Go module
|
||||
directory and a repository-root profile. The library derives the release tag
|
||||
from the selected module declaration and physical location; the table only
|
||||
selects the corresponding profile and checkout directory.
|
||||
|
||||
Copy the contents of this directory into a consumer repository root, keeping
|
||||
the listed module directories and profile paths at those locations, then run
|
||||
the launcher from that checkout.
|
||||
|
||||
`release-modules.tsv` is literal tab-separated text with four columns:
|
||||
|
||||
1. Local selector.
|
||||
2. Shell pattern matched against a complete event tag.
|
||||
3. Repository-relative physical module directory (`.` means the root module).
|
||||
4. Repository-relative profile path.
|
||||
|
||||
The checked-in patterns are `v[01].*`, `integrations/ckconfig/v*`, and
|
||||
`integrations/core/v2.*`. The helper passes pattern text to the shell's `case`
|
||||
matcher without `eval`. A tag must match exactly one row; missing and ambiguous
|
||||
matches are configuration errors. Module and profile paths may contain spaces.
|
||||
The `core-v2` row places the Go module physically under `integrations/core/v2`
|
||||
and declares a `/v2` module path. ReleaseKit derives the versioned tag under
|
||||
`integrations/core`, while the table retains the full physical path.
|
||||
|
||||
Before running candidate checks offline, prepare each module's dependencies in
|
||||
its own directory:
|
||||
|
||||
```sh
|
||||
go -C . mod download
|
||||
go -C integrations/ckconfig mod download
|
||||
go -C integrations/core/v2 mod download
|
||||
```
|
||||
|
||||
Then select a module and semantic version with the shared launcher:
|
||||
|
||||
```sh
|
||||
./release.sh root v1.2.3
|
||||
./release.sh ckconfig v1.2.3
|
||||
./release.sh core-v2 v2.1.0
|
||||
```
|
||||
|
||||
The launcher copies the selected module's `go.mod` and `go.sum` to temporary
|
||||
alternate module files outside the checkout, resolves ReleaseKit from that
|
||||
module's local dependency selection, and builds the tool there. It invokes
|
||||
`publish` with the repository profile and `--expected-module` so the library
|
||||
can verify that the profile agrees with the selected table row. The temporary
|
||||
files and tool are removed on exit. As with any `publish` invocation, commit the
|
||||
selected module's release note and prepare its repository before publishing.
|
||||
|
||||
If an integration depends on a sibling release, publish the dependency first,
|
||||
then publish the integration with the version that consumes it. For example,
|
||||
publish the core release before an integration that requires that release.
|
||||
This declaration does not infer dependency order or rerun the standard suite.
|
||||
5
examples/multi-module/go.mod
Normal file
5
examples/multi-module/go.mod
Normal file
@@ -0,0 +1,5 @@
|
||||
module example.com/multi-module-consumer
|
||||
|
||||
go 1.25.0
|
||||
|
||||
require gitea.maximumdirect.net/eric/ReleaseKit v0.2.197
|
||||
5
examples/multi-module/integrations/ckconfig/go.mod
Normal file
5
examples/multi-module/integrations/ckconfig/go.mod
Normal file
@@ -0,0 +1,5 @@
|
||||
module example.com/multi-module-consumer/integrations/ckconfig
|
||||
|
||||
go 1.25.0
|
||||
|
||||
require gitea.maximumdirect.net/eric/ReleaseKit v0.2.197
|
||||
5
examples/multi-module/integrations/core/v2/go.mod
Normal file
5
examples/multi-module/integrations/core/v2/go.mod
Normal file
@@ -0,0 +1,5 @@
|
||||
module example.com/multi-module-consumer/integrations/core/v2
|
||||
|
||||
go 1.25.0
|
||||
|
||||
require gitea.maximumdirect.net/eric/ReleaseKit v0.2.197
|
||||
4
examples/multi-module/release-modules.tsv
Normal file
4
examples/multi-module/release-modules.tsv
Normal file
@@ -0,0 +1,4 @@
|
||||
# selector<TAB>tag pattern<TAB>module directory<TAB>profile path
|
||||
root v[01].* . releasekit-root.json
|
||||
ckconfig integrations/ckconfig/v* integrations/ckconfig releasekit-ckconfig.json
|
||||
core-v2 integrations/core/v2.* integrations/core/v2 releasekit-core-v2.json
|
||||
|
48
examples/multi-module/release.sh
Executable file
48
examples/multi-module/release.sh
Executable file
@@ -0,0 +1,48 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
usage() {
|
||||
echo "usage: release.sh SELECTOR VERSION" >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
[ "$#" -eq 2 ] || usage
|
||||
selector=$1
|
||||
version=$2
|
||||
script_dir=$(CDPATH= cd "$(dirname "$0")" && pwd)
|
||||
repository_dir=$(git -C "$script_dir" rev-parse --show-toplevel)
|
||||
|
||||
bootstrap_dir=$(mktemp -d "$(dirname "$repository_dir")/.releasekit-bootstrap.XXXXXX")
|
||||
selection_file=$bootstrap_dir/module-selection.tsv
|
||||
trap 'rm -rf "$bootstrap_dir"' EXIT
|
||||
"$script_dir/select-module.sh" selector "$selector" > "$selection_file"
|
||||
|
||||
tab=$(printf '\t')
|
||||
IFS="$tab" read -r selected_selector tag_pattern module_dir profile_path < "$selection_file"
|
||||
module_path=$repository_dir
|
||||
if [ "$module_dir" != . ]; then
|
||||
module_path=$repository_dir/$module_dir
|
||||
fi
|
||||
|
||||
cp "$module_path/go.mod" "$bootstrap_dir/releasekit.mod"
|
||||
if [ -f "$module_path/go.sum" ]; then
|
||||
cp "$module_path/go.sum" "$bootstrap_dir/releasekit.sum"
|
||||
else
|
||||
: > "$bootstrap_dir/releasekit.sum"
|
||||
fi
|
||||
|
||||
releasekit_module=gitea.maximumdirect.net/eric/ReleaseKit
|
||||
(
|
||||
cd "$module_path"
|
||||
export GOWORK=off
|
||||
export GOTOOLCHAIN=local
|
||||
go list -mod=mod -modfile="$bootstrap_dir/releasekit.mod" -m "$releasekit_module"
|
||||
go build -mod=mod -modfile="$bootstrap_dir/releasekit.mod" \
|
||||
-o "$bootstrap_dir/releasekit" "$releasekit_module/cmd/releasekit"
|
||||
)
|
||||
|
||||
"$bootstrap_dir/releasekit" publish \
|
||||
--repo "$repository_dir" \
|
||||
--config "$profile_path" \
|
||||
--expected-module "$module_dir" \
|
||||
"$version"
|
||||
4
examples/multi-module/releasekit-ckconfig.json
Normal file
4
examples/multi-module/releasekit-ckconfig.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"product": "Example Consumer Integration",
|
||||
"module_dir": "integrations/ckconfig"
|
||||
}
|
||||
4
examples/multi-module/releasekit-core-v2.json
Normal file
4
examples/multi-module/releasekit-core-v2.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"product": "Example Consumer Core v2",
|
||||
"module_dir": "integrations/core/v2"
|
||||
}
|
||||
4
examples/multi-module/releasekit-root.json
Normal file
4
examples/multi-module/releasekit-root.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"product": "Example Consumer",
|
||||
"module_dir": "."
|
||||
}
|
||||
70
examples/multi-module/select-module.sh
Executable file
70
examples/multi-module/select-module.sh
Executable file
@@ -0,0 +1,70 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
usage() {
|
||||
echo "usage: select-module.sh selector|tag VALUE" >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
[ "$#" -eq 2 ] || usage
|
||||
mode=$1
|
||||
value=$2
|
||||
case "$mode" in
|
||||
selector|tag) ;;
|
||||
*) usage ;;
|
||||
esac
|
||||
|
||||
script_dir=$(CDPATH= cd "$(dirname "$0")" && pwd)
|
||||
mapping=${RELEASEKIT_MODULE_MAP:-$script_dir/release-modules.tsv}
|
||||
[ -f "$mapping" ] || { echo "module map not found: $mapping" >&2; exit 2; }
|
||||
|
||||
tab=$(printf '\t')
|
||||
matches=0
|
||||
selected_selector=
|
||||
selected_pattern=
|
||||
selected_module=
|
||||
selected_profile=
|
||||
|
||||
while IFS="$tab" read -r selector pattern module profile || [ -n "${selector:-}${pattern:-}${module:-}${profile:-}" ]; do
|
||||
case "${selector:-}" in
|
||||
''|\#*) continue ;;
|
||||
esac
|
||||
fields=$(awk -F '\t' '{ print NF }' <<EOF
|
||||
$selector$tab$pattern$tab$module$tab$profile
|
||||
EOF
|
||||
)
|
||||
[ "$fields" -eq 4 ] || { echo "module map row must have four tab-separated fields" >&2; exit 2; }
|
||||
[ -n "$pattern" ] && [ -n "$module" ] && [ -n "$profile" ] || {
|
||||
echo "module map row has an empty field for selector $selector" >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
matched=false
|
||||
if [ "$mode" = selector ]; then
|
||||
[ "$selector" = "$value" ] && matched=true
|
||||
else
|
||||
# The value remains a shell pattern in this case statement; it is never eval'd.
|
||||
case "$value" in
|
||||
$pattern) matched=true ;;
|
||||
esac
|
||||
fi
|
||||
if [ "$matched" = true ]; then
|
||||
matches=$((matches + 1))
|
||||
selected_selector=$selector
|
||||
selected_pattern=$pattern
|
||||
selected_module=$module
|
||||
selected_profile=$profile
|
||||
fi
|
||||
done < "$mapping"
|
||||
|
||||
if [ "$matches" -ne 1 ]; then
|
||||
if [ "$matches" -eq 0 ]; then
|
||||
echo "no module map row matches $mode $value" >&2
|
||||
else
|
||||
echo "module map has ambiguous rows for $mode $value" >&2
|
||||
fi
|
||||
exit 2
|
||||
fi
|
||||
|
||||
printf '%s\t%s\t%s\t%s\n' \
|
||||
"$selected_selector" "$selected_pattern" "$selected_module" "$selected_profile"
|
||||
Reference in New Issue
Block a user