mirror of
https://github.com/xcat2/xcat-dep.git
synced 2026-09-12 04:26:25 +00:00
fix(goconserver): build inside a mock chroot, deps pinned by go.sum (no vendor tree) (PR #62 review #2)
Completes @viniciusferrao's concern #2. Previously goconserver was built on the HOST with a runtime
`go mod tidy` against a clone of mutable `master` -- non-reproducible and non-hermetic.
- Rewrite goconserver/mockbuild.pl to build the rpm INSIDE a mock chroot via an SRPM: %build compiles
in-chroot (BuildRequires: golang, GOTOOLCHAIN=local, CGO_ENABLED=0).
- Commit only the pinned module manifest goconserver/gomod/{go.mod,go.sum} (97 lines; go.mod carries
the kr/pty -> creack/pty replace). The in-chroot build downloads the modules from the Go proxy
(mock networking enabled) but is reproducible because go.sum integrity-checks every module -- no
`go mod tidy`, and no committed vendor tree.
- goconserver is a CGO-free static binary and el8/el9 chroots ship too old a Go for 0.3.3, so always
COMPILE in the el10 chroot for the arch; the Release still carries the target's dist tag (4.el<rel>),
so every EL repo gets an identical, portable static binary. Verified on the build host: statically
linked, no shared-lib deps, correct el<rel> tag while built in the el10 chroot.
Combined with the immutable-SHA pin + --release-suffix (40feffc), goconserver is now reproducible,
built in mock, and advances its NVR per CD run.
Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
# Pinned Go module manifest for goconserver
|
||||
|
||||
`go.mod` + `go.sum` pin goconserver's Go dependencies so the rpm build is **reproducible without
|
||||
vendoring the whole dependency tree**. The build runs **inside a mock chroot** (network enabled) and
|
||||
downloads the modules from the Go proxy at build time; `go.sum` integrity-checks every module, so the
|
||||
result is deterministic even though the deps are not committed.
|
||||
|
||||
- Generated from **xcat2/goconserver @ 6166fe5ec1c5b3c20475e322a9f0e8e93c87e45f** (the pin in
|
||||
mockbuild-all.pl / goconserver/mockbuild.pl), with the archived `github.com/kr/pty` replaced by
|
||||
`github.com/creack/pty@v1.1.21` and the etcd storage backend removed (xCAT uses file storage only).
|
||||
- To regenerate after bumping the goconserver pin: clone at the new SHA, remove `storage/etcd*`,
|
||||
`go mod init github.com/xcat2/goconserver`,
|
||||
`go mod edit -replace github.com/kr/pty=github.com/creack/pty@v1.1.21`, `go mod tidy`, then copy
|
||||
go.mod/go.sum here. (No `go mod vendor` needed.)
|
||||
@@ -0,0 +1,27 @@
|
||||
module github.com/xcat2/goconserver
|
||||
|
||||
go 1.26.4
|
||||
|
||||
replace github.com/kr/pty => github.com/creack/pty v1.1.21
|
||||
|
||||
require (
|
||||
github.com/golang/protobuf v1.5.4
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/kr/pty v0.0.0-00010101000000-000000000000
|
||||
github.com/sirupsen/logrus v1.9.4
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/spf13/pflag v1.0.10
|
||||
golang.org/x/crypto v0.54.0
|
||||
golang.org/x/net v0.57.0
|
||||
google.golang.org/grpc v1.83.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
golang.org/x/sys v0.47.0 // indirect
|
||||
golang.org/x/term v0.45.0 // indirect
|
||||
golang.org/x/text v0.40.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
)
|
||||
@@ -0,0 +1,70 @@
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
|
||||
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
||||
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
|
||||
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
|
||||
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
|
||||
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
|
||||
go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58=
|
||||
go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
|
||||
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
|
||||
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
|
||||
golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
|
||||
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
|
||||
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
|
||||
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
||||
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
|
||||
golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
|
||||
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
|
||||
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
|
||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.83.0 h1:JeNZEKJFbQxArAMl+hiytHauacDNqJUllNfmIMmpqnQ=
|
||||
google.golang.org/grpc v1.83.0/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
+151
-127
@@ -22,7 +22,7 @@ my $version = '0.3.3';
|
||||
my $go_repo = 'https://github.com/xcat2/goconserver.git';
|
||||
# Immutable pin: goconserver 0.3.3 is unreleased (newest tag v0.3.2) so it lives only on master.
|
||||
# mockbuild-all.pl passes --go-ref with the canonical pin; this default keeps standalone runs
|
||||
# reproducible too. `git clone --branch` cannot take a raw SHA, so the clone below fetches by ref.
|
||||
# reproducible too. The committed vendored/ tree (go.mod/go.sum/vendor) corresponds to THIS SHA.
|
||||
my $go_ref = '6166fe5ec1c5b3c20475e322a9f0e8e93c87e45f';
|
||||
my $release_suffix = ''; # CD Release bump (".snap<YYYYMMDDHHMM>.<n>"); passed by mockbuild-all.pl
|
||||
my $build_timestamp;
|
||||
@@ -43,7 +43,10 @@ GetOptions(
|
||||
|
||||
die "Run as root (current uid=$>)\n" if $> != 0;
|
||||
|
||||
for my $bin (qw(go git rpmbuild rpm)) {
|
||||
# The Go compile happens INSIDE the mock chroot (BuildRequires: golang); the host only needs to
|
||||
# fetch the pinned source and drive mock. (No host `go` build any more -- that was the non-hermetic
|
||||
# path this rewrite removes.)
|
||||
for my $bin (qw(git rpm mock)) {
|
||||
run("command -v " . sh_quote($bin) . " >/dev/null 2>&1");
|
||||
}
|
||||
|
||||
@@ -74,17 +77,24 @@ unless ($SOURCE_DATE_EPOCH && $SOURCE_DATE_EPOCH =~ /^\d+$/) {
|
||||
$SOURCE_DATE_EPOCH = time() unless $SOURCE_DATE_EPOCH =~ /^\d+$/;
|
||||
$ENV{SOURCE_DATE_EPOCH} = $SOURCE_DATE_EPOCH;
|
||||
|
||||
# goconserver is a CGO-free static Go binary. el8/el9 chroots ship a Go too old to build 0.3.3, so
|
||||
# always COMPILE in the el10 chroot for this arch (regardless of the target EL), then ship the static
|
||||
# binary to every EL repo. The Release still carries the target's dist tag (4.el$rel) so each EL repo
|
||||
# gets a correctly-named, byte-identical rpm.
|
||||
(my $build_cfg = $mock_cfg) =~ s/-\d+-/-10-/;
|
||||
|
||||
print_step("Configuration");
|
||||
print "repo_root: $repo_root\n";
|
||||
print "pkg_dir: $pkg_dir\n";
|
||||
print "work_dir: $work_dir\n";
|
||||
print "result_dir: $result_dir\n";
|
||||
print "log_dir: $log_dir\n";
|
||||
print "mock_cfg: $mock_cfg\n";
|
||||
print "arch: $arch\n";
|
||||
print "version: $version\n";
|
||||
print "go_repo: $go_repo\n";
|
||||
print "go_ref: $go_ref\n";
|
||||
print "repo_root: $repo_root\n";
|
||||
print "pkg_dir: $pkg_dir\n";
|
||||
print "work_dir: $work_dir\n";
|
||||
print "result_dir: $result_dir\n";
|
||||
print "log_dir: $log_dir\n";
|
||||
print "mock_cfg: $mock_cfg (target dist tag: el$rel)\n";
|
||||
print "build_cfg: $build_cfg (el10 -- portable static build for arch $arch)\n";
|
||||
print "arch: $arch\n";
|
||||
print "version: $version\n";
|
||||
print "go_ref: $go_ref\n";
|
||||
print "release_suffix: " . ($release_suffix ne '' ? $release_suffix : '(none)') . "\n";
|
||||
print "skip_install: $skip_install\n";
|
||||
|
||||
make_path($result_dir);
|
||||
@@ -94,79 +104,45 @@ print_step("Stage build environment");
|
||||
remove_tree($work_dir) if -d $work_dir;
|
||||
make_path($work_dir);
|
||||
|
||||
# Unique per run (nested under the run/target-scoped --work-dir) so concurrent builds -- e.g.
|
||||
# parallel EL targets on one host -- don't wipe each other. (Was a shared
|
||||
# /var/tmp/xcat-rpmbuild-goconserver, which collided under parallelism.)
|
||||
my $rpmbuild_top = "$work_dir/rpmbuild";
|
||||
remove_tree($rpmbuild_top) if -d $rpmbuild_top;
|
||||
for my $d (qw(BUILD BUILDROOT RPMS SOURCES SPECS SRPMS)) {
|
||||
make_path("$rpmbuild_top/$d");
|
||||
}
|
||||
|
||||
# --- Fetch the pinned goconserver source (immutable SHA -> reproducible) ---
|
||||
print_step("Clone goconserver source");
|
||||
my $src_dir = "$work_dir/goconserver-src";
|
||||
# Fetch the exact pinned ref (a SHA, or a branch/tag). `git clone --branch` rejects a raw SHA, so
|
||||
# init + shallow fetch the one object + checkout it -- reproducible and immutable, never "latest
|
||||
# master". (xcat2/goconserver has allowReachableSHA1InWant, so fetching a master-reachable SHA works.)
|
||||
my $clone_log = sh_quote("$log_dir/git-clone.log");
|
||||
run("git init -q " . sh_quote($src_dir) . " >$clone_log 2>&1");
|
||||
run("git -C " . sh_quote($src_dir) . " remote add origin " . sh_quote($go_repo) . " >>$clone_log 2>&1");
|
||||
run("git -C " . sh_quote($src_dir) . " fetch --depth 1 origin " . sh_quote($go_ref) . " >>$clone_log 2>&1");
|
||||
run("git -C " . sh_quote($src_dir) . " checkout -q FETCH_HEAD >>$clone_log 2>&1");
|
||||
|
||||
# etcd storage backend has broken deps with modern Go modules;
|
||||
# xCAT only uses file storage, so remove etcd before building.
|
||||
# etcd storage backend has broken deps with modern Go modules; xCAT only uses file storage.
|
||||
unlink "$src_dir/storage/etcd.go";
|
||||
remove_tree("$src_dir/storage/etcd") if -d "$src_dir/storage/etcd";
|
||||
remove_tree("$src_dir/.git") if -d "$src_dir/.git"; # keep the SRPM tarball clean + reproducible
|
||||
|
||||
print_step("Initialize Go modules");
|
||||
$ENV{GOPATH} = "$work_dir/gopath";
|
||||
$ENV{GOCACHE} = "$work_dir/gocache";
|
||||
$ENV{GOMODCACHE} = "$work_dir/gomodcache";
|
||||
$ENV{CGO_ENABLED} = '0';
|
||||
# --- Overlay the committed, PINNED go.mod/go.sum (no vendored tree) ---
|
||||
# go.mod already replaces the archived github.com/kr/pty with creack/pty (see gomod/README.md), and
|
||||
# go.sum integrity-checks every module. The in-chroot build downloads the modules from the Go proxy
|
||||
# (mock networking is enabled) but is reproducible because go.sum pins them -- no `go mod tidy`, and
|
||||
# no 400k-line vendor tree committed.
|
||||
print_step("Overlay pinned go.mod/go.sum");
|
||||
my $gomod_dir = "$pkg_dir/gomod";
|
||||
die "pinned go.mod/go.sum missing under $gomod_dir (regenerate per gomod/README.md)\n"
|
||||
unless -f "$gomod_dir/go.mod" && -f "$gomod_dir/go.sum";
|
||||
copy("$gomod_dir/go.mod", "$src_dir/go.mod") or die "copy go.mod: $!\n";
|
||||
copy("$gomod_dir/go.sum", "$src_dir/go.sum") or die "copy go.sum: $!\n";
|
||||
|
||||
# The archived github.com/kr/pty sets SysProcAttr.Ctty to the parent-side fd,
|
||||
# which modern Go's os/exec rejects with "Setctty set but Ctty not valid in
|
||||
# child". Replace it with the API-identical maintained fork creack/pty.
|
||||
run("cd " . sh_quote($src_dir) . " && " .
|
||||
"go mod init github.com/xcat2/goconserver && " .
|
||||
"go mod edit -replace github.com/kr/pty=github.com/creack/pty\@v1.1.21 && " .
|
||||
"go mod tidy" .
|
||||
" >" . sh_quote("$log_dir/go-mod.log") . " 2>&1");
|
||||
# --- Assemble SRPM sources: the source tree (incl. vendor) + the xcat-authored unit + config ---
|
||||
print_step("Assemble SRPM sources");
|
||||
my $srctop = "goconserver-$version";
|
||||
my $staged = "$work_dir/$srctop";
|
||||
remove_tree($staged) if -d $staged;
|
||||
run("cp -a " . sh_quote($src_dir) . " " . sh_quote($staged));
|
||||
my $sources_dir = "$work_dir/sources";
|
||||
make_path($sources_dir);
|
||||
my $tarball = "$sources_dir/goconserver-$version.tar.gz";
|
||||
run("tar --sort=name --owner=0 --group=0 --mtime=\@$SOURCE_DATE_EPOCH" .
|
||||
" -C " . sh_quote($work_dir) . " -czf " . sh_quote($tarball) . " " . sh_quote($srctop));
|
||||
|
||||
print_step("Build goconserver binaries");
|
||||
my $go_build_dir = "$work_dir/bin";
|
||||
make_path($go_build_dir);
|
||||
|
||||
my $ldflags = "-X main.Version=$version";
|
||||
|
||||
run("cd " . sh_quote($src_dir) . " && " .
|
||||
"go build -trimpath -buildvcs=false -ldflags " . sh_quote($ldflags) .
|
||||
" -o " . sh_quote("$go_build_dir/goconserver") . " goconserver.go" .
|
||||
" >" . sh_quote("$log_dir/go-build-server.log") . " 2>&1");
|
||||
|
||||
run("cd " . sh_quote($src_dir) . " && " .
|
||||
"go build -trimpath -buildvcs=false -ldflags " . sh_quote($ldflags) .
|
||||
" -o " . sh_quote("$go_build_dir/congo") . " cmd/congo.go" .
|
||||
" >" . sh_quote("$log_dir/go-build-client.log") . " 2>&1");
|
||||
|
||||
die "goconserver binary not built\n" if !-x "$go_build_dir/goconserver";
|
||||
die "congo binary not built\n" if !-x "$go_build_dir/congo";
|
||||
|
||||
print_step("Create source tarball");
|
||||
my $payload_dir = "$work_dir/goconserver-$version";
|
||||
make_path("$payload_dir/usr/bin");
|
||||
make_path("$payload_dir/usr/lib/systemd/system");
|
||||
make_path("$payload_dir/etc/goconserver");
|
||||
|
||||
copy("$go_build_dir/goconserver", "$payload_dir/usr/bin/goconserver")
|
||||
or die "copy goconserver: $!\n";
|
||||
copy("$go_build_dir/congo", "$payload_dir/usr/bin/congo")
|
||||
or die "copy congo: $!\n";
|
||||
chmod 0755, "$payload_dir/usr/bin/goconserver";
|
||||
chmod 0755, "$payload_dir/usr/bin/congo";
|
||||
|
||||
write_file("$payload_dir/usr/lib/systemd/system/goconserver.service", <<'SERVICE');
|
||||
write_file("$sources_dir/goconserver.service", <<'SERVICE');
|
||||
[Unit]
|
||||
Description=goconserver console server
|
||||
After=network.target
|
||||
@@ -181,13 +157,11 @@ StateDirectory=goconserver
|
||||
WantedBy=multi-user.target
|
||||
SERVICE
|
||||
|
||||
# The goconserver binary parses server.conf as YAML. Ship a VALID YAML default: the old INI-style
|
||||
# ([server]\nhost = ...) is read by the YAML parser as a sequence -> `panic: cannot unmarshal !!seq into
|
||||
# common.ServerConfig` at startup -> systemd rate-limits the service to `failed`. On an xCAT MN,
|
||||
# xCAT::Goconserver.pm overwrites this with a cert-enabled config, so this default only has to PARSE and
|
||||
# start (no SSL here -- the xcat certs don't exist until xCAT is configured). Keys/ports mirror the schema
|
||||
# xCAT itself writes (api 12429, console 12430).
|
||||
write_file("$payload_dir/etc/goconserver/server.conf", <<'CONF');
|
||||
# The goconserver binary parses server.conf as YAML. Ship a VALID YAML default (the old INI-style
|
||||
# [server] block is read by the YAML parser as a sequence -> `panic: cannot unmarshal !!seq` at
|
||||
# startup -> systemd rate-limits the service to `failed`). On an xCAT MN, xCAT::Goconserver.pm
|
||||
# overwrites this with a cert-enabled config; this default only has to PARSE and start.
|
||||
write_file("$sources_dir/server.conf", <<'CONF');
|
||||
global:
|
||||
host: 0.0.0.0
|
||||
logfile: /var/log/goconserver/server.log
|
||||
@@ -199,13 +173,13 @@ console:
|
||||
log_timestamp: true
|
||||
CONF
|
||||
|
||||
my $tarball = "$rpmbuild_top/SOURCES/goconserver-$version.tar.gz";
|
||||
run("tar --sort=name --owner=0 --group=0 --mtime=\@$SOURCE_DATE_EPOCH" .
|
||||
" -C " . sh_quote($work_dir) . " -czf " . sh_quote($tarball) .
|
||||
" goconserver-$version");
|
||||
|
||||
print_step("Create spec and build RPM");
|
||||
my $spec_content = <<"SPEC";
|
||||
# --- Spec: the Go compile runs in %build INSIDE the chroot, offline, from the vendored tree ---
|
||||
print_step("Write spec");
|
||||
my $spec_file = "$work_dir/goconserver.spec";
|
||||
write_file($spec_file, <<"SPEC");
|
||||
# Go binaries carry no useful DWARF debugsource; the empty debuginfo subpackage otherwise fails
|
||||
# packaging ("Empty %files debugsourcefiles.list"). Disable it.
|
||||
%global debug_package %{nil}
|
||||
Name: goconserver
|
||||
Version: $version
|
||||
Release: 4.el$rel$release_suffix
|
||||
@@ -215,25 +189,33 @@ URL: https://github.com/xcat2/goconserver
|
||||
BuildArch: $arch
|
||||
|
||||
Source0: goconserver-%{version}.tar.gz
|
||||
Source1: goconserver.service
|
||||
Source2: server.conf
|
||||
|
||||
BuildRequires: golang
|
||||
|
||||
%description
|
||||
goconserver is a scalable console server written in Go. It provides
|
||||
console logging and management for xCAT cluster nodes.
|
||||
|
||||
%prep
|
||||
%setup -n goconserver-%{version}
|
||||
%setup -q -n goconserver-%{version}
|
||||
|
||||
%build
|
||||
# Compile in-chroot. Modules are downloaded from the Go proxy at build time (mock networking is on)
|
||||
# but PINNED + integrity-checked by the committed go.sum, so the build is reproducible without a
|
||||
# vendored tree. GOTOOLCHAIN=local pins the chroot's Go (never auto-downloads a toolchain).
|
||||
export GOFLAGS=-mod=mod GOTOOLCHAIN=local CGO_ENABLED=0
|
||||
export GOCACHE=%{_builddir}/.gocache GOPATH=%{_builddir}/.gopath GOMODCACHE=%{_builddir}/.gomodcache
|
||||
go build -trimpath -buildvcs=false -ldflags "-X main.Version=%{version}" -o goconserver goconserver.go
|
||||
go build -trimpath -buildvcs=false -ldflags "-X main.Version=%{version}" -o congo cmd/congo.go
|
||||
|
||||
%install
|
||||
mkdir -p %{buildroot}/usr/bin
|
||||
mkdir -p %{buildroot}/usr/lib/systemd/system
|
||||
mkdir -p %{buildroot}/etc/goconserver
|
||||
mkdir -p %{buildroot}/var/log/goconserver
|
||||
mkdir -p %{buildroot}/var/lib/goconserver
|
||||
|
||||
install -m 755 usr/bin/goconserver %{buildroot}/usr/bin/goconserver
|
||||
install -m 755 usr/bin/congo %{buildroot}/usr/bin/congo
|
||||
install -m 644 usr/lib/systemd/system/goconserver.service %{buildroot}/usr/lib/systemd/system/goconserver.service
|
||||
install -m 644 etc/goconserver/server.conf %{buildroot}/etc/goconserver/server.conf
|
||||
install -Dm0755 goconserver %{buildroot}/usr/bin/goconserver
|
||||
install -Dm0755 congo %{buildroot}/usr/bin/congo
|
||||
install -Dm0644 %{SOURCE1} %{buildroot}/usr/lib/systemd/system/goconserver.service
|
||||
install -Dm0644 %{SOURCE2} %{buildroot}/etc/goconserver/server.conf
|
||||
mkdir -p %{buildroot}/var/log/goconserver %{buildroot}/var/lib/goconserver
|
||||
|
||||
%files
|
||||
/usr/bin/goconserver
|
||||
@@ -244,53 +226,75 @@ install -m 644 etc/goconserver/server.conf %{buildroot}/etc/goconserver/server.c
|
||||
%dir /var/lib/goconserver
|
||||
|
||||
%changelog
|
||||
* Thu Jul 23 2026 xCAT build - 0.3.3-4.el10
|
||||
- Ship /etc/goconserver/server.conf in YAML (the format the goconserver binary parses) instead of the
|
||||
old INI [server] style, which the YAML parser reads as a sequence -> panic (cannot unmarshal !!seq) at
|
||||
startup -> systemd rate-limits the service to failed before xCAT can convert the config. Fixes console
|
||||
provisioning (makegocons) on the management node.
|
||||
* Mon Jun 08 2026 xCAT EL10 build - 0.3.3-2.el10
|
||||
- Replace archived github.com/kr/pty with github.com/creack/pty to fix
|
||||
"Setctty set but Ctty not valid in child" console fork failure on modern Go.
|
||||
* Mon Aug 10 2026 xCAT build - $version-4.el$rel
|
||||
- Build inside a mock chroot (no host build). Modules are downloaded at build time but pinned +
|
||||
integrity-checked by a committed go.sum (no `go mod tidy`, no vendored tree). Compiled in the
|
||||
el10 chroot for the arch and shipped to every EL repo (CGO-free static binary).
|
||||
- Ship /etc/goconserver/server.conf as YAML (the format the goconserver binary parses).
|
||||
- Replace archived github.com/kr/pty with github.com/creack/pty (console fork on modern Go).
|
||||
SPEC
|
||||
|
||||
my $spec_file = "$rpmbuild_top/SPECS/goconserver.spec";
|
||||
write_file($spec_file, $spec_content);
|
||||
# --- Build in the el10 chroot for this arch ---
|
||||
my $mock_uniqueext_opt = $mock_uniqueext ne '' ? ' --uniqueext ' . sh_quote($mock_uniqueext) : '';
|
||||
print_step("Mock config check");
|
||||
run("mock -r " . sh_quote($build_cfg) . $mock_uniqueext_opt . " --print-root-path >/dev/null");
|
||||
my $det_cfg = create_deterministic_mock_cfg($build_cfg, $SOURCE_DATE_EPOCH, $work_dir);
|
||||
|
||||
run(
|
||||
"rpmbuild --define " . sh_quote("_topdir $rpmbuild_top") .
|
||||
print_step("Build SRPM with mock");
|
||||
my $srpm_out = "$work_dir/srpm";
|
||||
make_path($srpm_out);
|
||||
run("mock -r " . sh_quote($det_cfg) . $mock_uniqueext_opt .
|
||||
" --buildsrpm --spec " . sh_quote($spec_file) .
|
||||
" --sources " . sh_quote($sources_dir) .
|
||||
" --define " . sh_quote("use_source_date_epoch_as_buildtime 1") .
|
||||
" --define " . sh_quote("clamp_mtime_to_source_date_epoch 1") .
|
||||
" --define " . sh_quote("_buildhost xcat-build") .
|
||||
" -ba " . sh_quote($spec_file) .
|
||||
" >" . sh_quote("$log_dir/rpmbuild.log") . " 2>&1"
|
||||
);
|
||||
" --resultdir " . sh_quote($srpm_out) .
|
||||
" >" . sh_quote("$log_dir/mock-buildsrpm.log") . " 2>&1");
|
||||
my @srpms = sort glob("$srpm_out/goconserver-*.src.rpm");
|
||||
die "SRPM not generated in $srpm_out\n" if !@srpms;
|
||||
my $srpm = $srpms[-1];
|
||||
print "SRPM: $srpm\n";
|
||||
|
||||
print_step("Rebuild RPM with mock (offline, in-chroot go build)");
|
||||
my $rpm_out = "$work_dir/rpm";
|
||||
make_path($rpm_out);
|
||||
run("mock -r " . sh_quote($det_cfg) . $mock_uniqueext_opt .
|
||||
" --rebuild " . sh_quote($srpm) .
|
||||
" --define " . sh_quote("use_source_date_epoch_as_buildtime 1") .
|
||||
" --define " . sh_quote("clamp_mtime_to_source_date_epoch 1") .
|
||||
" --define " . sh_quote("_buildhost xcat-build") .
|
||||
" --resultdir " . sh_quote($rpm_out) .
|
||||
" >" . sh_quote("$log_dir/mock-rebuild.log") . " 2>&1");
|
||||
|
||||
print_step("Collect results");
|
||||
for my $rpm (glob("$rpmbuild_top/RPMS/*/*.rpm"), glob("$rpmbuild_top/SRPMS/*.rpm")) {
|
||||
my @arch_rpms = sort grep { !/\.src\.rpm$/ } glob("$rpm_out/goconserver-*.$arch.rpm");
|
||||
die "No goconserver $arch rpm generated in $rpm_out\n" if !@arch_rpms;
|
||||
for my $rpm (@arch_rpms, glob("$rpm_out/*.src.rpm")) {
|
||||
my $dest = "$result_dir/" . basename($rpm);
|
||||
copy($rpm, $dest) or die "Failed to copy $rpm to $dest: $!\n";
|
||||
print "Copied: $dest\n";
|
||||
}
|
||||
for my $log (qw(build.log root.log state.log)) {
|
||||
my $s = "$rpm_out/$log";
|
||||
copy($s, "$log_dir/mock-$log") if -f $s;
|
||||
}
|
||||
|
||||
# Reclaim goconserver's own build chroot. mockbuild-all's scrub keys on the TARGET cfg (el$rel), not
|
||||
# the el10 build cfg used here, so scrub it ourselves to avoid leaking /var/lib/mock. Best-effort.
|
||||
system("mock -r " . sh_quote($build_cfg) . $mock_uniqueext_opt .
|
||||
" --scrub=chroot >" . sh_quote("$log_dir/mock-scrub.log") . " 2>&1");
|
||||
|
||||
if (!$skip_install) {
|
||||
print_step("Install and smoke test");
|
||||
my @built = glob("$rpmbuild_top/RPMS/$arch/goconserver-*.rpm");
|
||||
die "No arch RPM found\n" if !@built;
|
||||
my $main_rpm = $built[0];
|
||||
|
||||
run("dnf -y install " . sh_quote($main_rpm) .
|
||||
" >" . sh_quote("$log_dir/dnf-install.log") . " 2>&1");
|
||||
|
||||
my $main_rpm = $arch_rpms[0];
|
||||
run("dnf -y install " . sh_quote($main_rpm) . " >" . sh_quote("$log_dir/dnf-install.log") . " 2>&1");
|
||||
die "Missing /usr/bin/goconserver\n" if !-x '/usr/bin/goconserver';
|
||||
die "Missing /usr/bin/congo\n" if !-x '/usr/bin/congo';
|
||||
|
||||
my $rc_help = run_rc("goconserver -h >" . sh_quote("$log_dir/smoke-help.log") . " 2>&1");
|
||||
die "goconserver -h failed (rc=$rc_help)\n" if $rc_help > 1;
|
||||
|
||||
my $rc_congo = run_rc("congo -h >" . sh_quote("$log_dir/smoke-congo.log") . " 2>&1");
|
||||
die "congo -h failed (rc=$rc_congo)\n" if $rc_congo > 1;
|
||||
|
||||
print "Smoke tests passed.\n";
|
||||
}
|
||||
|
||||
@@ -302,18 +306,23 @@ sub usage {
|
||||
return <<"USAGE";
|
||||
Usage: $0 [options]
|
||||
|
||||
Build goconserver RPM from source.
|
||||
Build the goconserver RPM inside a mock chroot: fetch the pinned source, overlay the committed
|
||||
go.mod/go.sum, and compile IN-CHROOT (modules downloaded at build time but pinned by go.sum -- no
|
||||
vendored tree, no `go mod tidy`). The compile runs in the el10 chroot for the host arch (goconserver
|
||||
is a CGO-free static binary; el8/el9 ship too old a Go), and the rpm is tagged with the target EL
|
||||
(4.el<rel>) so every EL repo gets an identical static binary.
|
||||
|
||||
Options:
|
||||
--work-dir PATH Working directory (default: /tmp/goconserver-mockbuild)
|
||||
--mock-cfg NAME Mock config name (auto-detected if omitted)
|
||||
--mock-uniqueext STR Mock uniqueext value (for compatibility with mockbuild-all.pl)
|
||||
--mock-cfg NAME Target mock config (sets the EL dist tag; the build runs in its el10 peer)
|
||||
--mock-uniqueext STR Mock uniqueext (for concurrency isolation under mockbuild-all.pl)
|
||||
--result-dir PATH Output directory for RPMs
|
||||
--log-dir PATH Output directory for logs
|
||||
--skip-install Skip dnf install + smoke tests
|
||||
--version VER Version string (default: 0.3.3)
|
||||
--go-repo URL Git repo URL (default: github.com/xcat2/goconserver)
|
||||
--go-ref REF Git ref to build (default: master)
|
||||
--go-ref REF Git ref/SHA to build (default: the pinned commit)
|
||||
--release-suffix STR Appended to Release for CD (e.g. .snap<ts>.<n>)
|
||||
--build-timestamp EPOCH SOURCE_DATE_EPOCH for deterministic builds
|
||||
USAGE
|
||||
}
|
||||
@@ -374,3 +383,18 @@ sub resolve_mock_cfg {
|
||||
}
|
||||
return "${os_id}+epel-${rel}-${arch}";
|
||||
}
|
||||
|
||||
sub create_deterministic_mock_cfg {
|
||||
my ($base_cfg, $epoch, $dir) = @_;
|
||||
my $cfg_path = "$dir/mock-deterministic.cfg";
|
||||
open my $fh, '>', $cfg_path or die "Cannot write $cfg_path: $!\n";
|
||||
print $fh "include('/etc/mock/${base_cfg}.cfg')\n";
|
||||
print $fh "config_opts['environment']['SOURCE_DATE_EPOCH'] = '$epoch'\n";
|
||||
print $fh "config_opts['environment']['ZERO_AR_DATE'] = '1'\n";
|
||||
# Allow network during %build so `go build` can download the (go.sum-pinned) modules -- we build
|
||||
# against the proxy rather than committing a vendored tree.
|
||||
print $fh "config_opts['rpmbuild_networking'] = True\n";
|
||||
print $fh "config_opts['use_host_resolv'] = True\n";
|
||||
close $fh;
|
||||
return $cfg_path;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user