build: pin line endings to LF so the embedded bootstrap.sh ships without CRs
The repository had no .gitattributes. With core.autocrlf=true a Windows checkout handed deploy/bootstrap.sh 2374 CRs, and bootstrap_asset.go embeds that file from the working tree verbatim, so a dev-built felis piped a CRLF script into `bash -s` on the target host. CI builds on Linux, which is why released binaries were clean and only local builds carried it. eol=lf is global rather than scoped to *.sh because go:embed reaches further than the installer: deploy/*/Dockerfile, deploy/*/entrypoint.sh, plugins/*/src, the migrations and internal/panel/static are all compiled in and read on Linux. *.bat is the one exception, for the gradle wrappers' Windows launchers. Renormalizing the index touched exactly one tracked file, cmd/felis/version.go, and only its line endings: `git diff --cached --ignore-cr-at-eol` reports nothing outside .gitattributes itself. TestBootstrapPinsViaBlockConnectionsOff used to strip \r\n before asserting, with a comment stating that the repository pinned no eol attribute. That is no longer true, and the stripping hid the regression this commit prevents. It now asserts the absence of CRs, so losing the attribute reports itself as line endings rather than as a missing serverside-blockconnections pin. Closes #5
This commit is contained in:
3 files changed
+83
-69
No files matched your search
@@ -0,0 +1,8 @@
|
|||||||
|
# Everything in this repository is consumed on Linux: deploy/bootstrap.sh is embedded
|
||||||
|
# verbatim by bootstrap_asset.go and piped into `bash -s`, the Dockerfiles and entrypoints
|
||||||
|
# are read inside the images, and the operator ships YAML. Without eol=lf a Windows
|
||||||
|
# checkout under core.autocrlf=true hands all of them CRs.
|
||||||
|
* text=auto eol=lf
|
||||||
|
|
||||||
|
# The gradle wrappers' Windows launchers are the one thing that wants CRLF.
|
||||||
|
*.bat text eol=crlf
|
||||||
@@ -73,9 +73,15 @@ func TestLobbyLuckPermsWiringIsConsistent(t *testing.T) {
|
|||||||
// default it can inherit — and nothing else in the install would notice it missing. The failure
|
// default it can inherit — and nothing else in the install would notice it missing. The failure
|
||||||
// surfaces only when a legacy player joins, on a host that installed cleanly.
|
// surfaces only when a legacy player joins, on a host that installed cleanly.
|
||||||
func TestBootstrapPinsViaBlockConnectionsOff(t *testing.T) {
|
func TestBootstrapPinsViaBlockConnectionsOff(t *testing.T) {
|
||||||
// go:embed takes the working tree verbatim, and this repository pins no eol attribute, so
|
// go:embed takes the working tree verbatim, so the eol attribute is what keeps a Windows
|
||||||
// a Windows checkout embeds CRLF. Only the assertion spanning a line break below cares.
|
// checkout from compiling CRs into the installer. Assert it rather than normalizing them
|
||||||
script := strings.ReplaceAll(BootstrapScript(), "\r\n", "\n")
|
// away: the only assertion that would otherwise notice is the one spanning a line break
|
||||||
|
// below, and it would report a missing pin instead of the line endings.
|
||||||
|
script := BootstrapScript()
|
||||||
|
if strings.Contains(script, "\r\n") {
|
||||||
|
t.Fatal("embedded bootstrap.sh has CRLF line endings; .gitattributes pins *.sh to LF " +
|
||||||
|
"and this script is piped into `bash -s` on a Linux host")
|
||||||
|
}
|
||||||
|
|
||||||
const key = "serverside-blockconnections"
|
const key = "serverside-blockconnections"
|
||||||
if !strings.Contains(script, key+": false") {
|
if !strings.Contains(script, key+": false") {
|
||||||
|
|||||||
+66
-66
@@ -1,66 +1,66 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
)
|
)
|
||||||
|
|
||||||
// version is the build stamp injected at link time via
|
// version is the build stamp injected at link time via
|
||||||
//
|
//
|
||||||
// -ldflags "-X main.version=v1.2.3" (release channel: the tag verbatim)
|
// -ldflags "-X main.version=v1.2.3" (release channel: the tag verbatim)
|
||||||
// -ldflags "-X main.version=v1.2.3+g1a2b3c4" (dev channel: tag + build metadata)
|
// -ldflags "-X main.version=v1.2.3+g1a2b3c4" (dev channel: tag + build metadata)
|
||||||
//
|
//
|
||||||
// deploy/bootstrap.sh computes it per install channel (FELIS_VERSION_BOOTSTRAP):
|
// deploy/bootstrap.sh computes it per install channel (FELIS_VERSION_BOOTSTRAP):
|
||||||
// the release channel stamps the resolved tag verbatim (v1.2.3), the dev channel
|
// the release channel stamps the resolved tag verbatim (v1.2.3), the dev channel
|
||||||
// stamps "<latest-tag>+g<short-sha>". It stays "dev" for an un-stamped local
|
// stamps "<latest-tag>+g<short-sha>". It stays "dev" for an un-stamped local
|
||||||
// `go build`, where ReadBuildInfo below still surfaces the vcs revision.
|
// `go build`, where ReadBuildInfo below still surfaces the vcs revision.
|
||||||
//
|
//
|
||||||
// NOT `git describe`, for two reasons that both bite. Its "<tag>-<n>-g<sha>" form
|
// NOT `git describe`, for two reasons that both bite. Its "<tag>-<n>-g<sha>" form
|
||||||
// puts the distance in the PRERELEASE field, which sorts BELOW the bare tag, so a
|
// puts the distance in the PRERELEASE field, which sorts BELOW the bare tag, so a
|
||||||
// dev build ahead of v1.2.3 would compare as older than v1.2.3 and `felis update`
|
// dev build ahead of v1.2.3 would compare as older than v1.2.3 and `felis update`
|
||||||
// would propose "upgrading" onto the release it already contains — hence "+", which
|
// would propose "upgrading" onto the release it already contains — hence "+", which
|
||||||
// is build metadata and ignored for ordering. And bootstrap's primary clone is
|
// is build metadata and ignored for ordering. And bootstrap's primary clone is
|
||||||
// --depth 1, which carries no tags, so describe would fall back to a bare SHA that
|
// --depth 1, which carries no tags, so describe would fall back to a bare SHA that
|
||||||
// updates.Parse rejects outright.
|
// updates.Parse rejects outright.
|
||||||
var version = "dev"
|
var version = "dev"
|
||||||
|
|
||||||
// cmdVersion prints the build stamp. It takes no flags and never touches the
|
// cmdVersion prints the build stamp. It takes no flags and never touches the
|
||||||
// cluster, so it is safe to run as any user (unlike setup/breakGlass).
|
// cluster, so it is safe to run as any user (unlike setup/breakGlass).
|
||||||
func cmdVersion(args []string, stdout, stderr io.Writer) int {
|
func cmdVersion(args []string, stdout, stderr io.Writer) int {
|
||||||
fmt.Fprintf(stdout, "felis %s\n", resolvedVersion())
|
fmt.Fprintf(stdout, "felis %s\n", resolvedVersion())
|
||||||
fmt.Fprintf(stdout, " go: %s %s/%s\n", runtime.Version(), runtime.GOOS, runtime.GOARCH)
|
fmt.Fprintf(stdout, " go: %s %s/%s\n", runtime.Version(), runtime.GOOS, runtime.GOARCH)
|
||||||
if rev, ok := vcsRevision(); ok {
|
if rev, ok := vcsRevision(); ok {
|
||||||
fmt.Fprintf(stdout, " revision: %s\n", rev)
|
fmt.Fprintf(stdout, " revision: %s\n", rev)
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolvedVersion prefers the ldflag stamp, then the module version recorded by
|
// resolvedVersion prefers the ldflag stamp, then the module version recorded by
|
||||||
// `go install`, and only reports "unknown" when neither is present.
|
// `go install`, and only reports "unknown" when neither is present.
|
||||||
func resolvedVersion() string {
|
func resolvedVersion() string {
|
||||||
if version != "" {
|
if version != "" {
|
||||||
return version
|
return version
|
||||||
}
|
}
|
||||||
if bi, ok := debug.ReadBuildInfo(); ok && bi.Main.Version != "" {
|
if bi, ok := debug.ReadBuildInfo(); ok && bi.Main.Version != "" {
|
||||||
return bi.Main.Version
|
return bi.Main.Version
|
||||||
}
|
}
|
||||||
return "unknown"
|
return "unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
// vcsRevision returns the git commit the binary was built from when the build
|
// vcsRevision returns the git commit the binary was built from when the build
|
||||||
// carried VCS stamping (local `go build` in a checkout; the docker build strips
|
// carried VCS stamping (local `go build` in a checkout; the docker build strips
|
||||||
// .git, so there the ldflag version carries the identity instead).
|
// .git, so there the ldflag version carries the identity instead).
|
||||||
func vcsRevision() (string, bool) {
|
func vcsRevision() (string, bool) {
|
||||||
bi, ok := debug.ReadBuildInfo()
|
bi, ok := debug.ReadBuildInfo()
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
for _, s := range bi.Settings {
|
for _, s := range bi.Settings {
|
||||||
if s.Key == "vcs.revision" && s.Value != "" {
|
if s.Key == "vcs.revision" && s.Value != "" {
|
||||||
return s.Value, true
|
return s.Value, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user