Files
fleet/orbit/pkg/packaging/bom_darwin_test.go
T
Lucas Manuel Rodriguez ada55da5bd Drop Docker fleetdm/bomutils dependency in fleetctl when generating pkg fleetd installers (#48915)
Resolves #48448.

These should help with reviewing the XAR and BOM implementations:
- https://claude.ai/code/artifact/60a78c1d-2fc9-45da-9471-1517fe77adb4.
- https://claude.ai/code/artifact/1c759a32-02f7-4a41-8611-04d7358367d7.

The darwin only tests (bom_darwin_test.go) have been executed on my
workstation.
Goal is to make sure to run the tests on macOS Github runners in
https://github.com/fleetdm/fleet/issues/33371.

- [X] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.

## Testing

- [x] QA'd all new/changed functionality manually

## fleetd/orbit/Fleet Desktop

- [x] Verified compatibility with the latest released version of Fleet
(see [Must
rule](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/workflows/fleetd-development-and-release-strategy.md))
- [x] Verified auto-update works from the released version of component
to the new version (see [tools/tuf/test](../tools/tuf/test/README.md))


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* macOS package builds now use an internal, built-in implementation
instead of external packaging tools.
* `.pkg` installer creation no longer depends on Docker for macOS
packaging.

* **Bug Fixes**
* Improved packaging reliability by reducing platform-specific build
steps.
* Packaging test coverage was streamlined to better match the supported
build environment.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-15 10:14:57 -03:00

86 lines
3.4 KiB
Go

//go:build darwin
package packaging
import (
"os"
"os/exec"
"path/filepath"
"regexp"
"sort"
"strings"
"testing"
"github.com/stretchr/testify/require"
)
// TestWriteBomMatchesMkbom builds a BOM two ways for the same tree -- via the
// native mkbom pipeline used by xarBom (mkbom -> lsbom -> 0/80 transform ->
// mkbom -i) and via the pure-Go writeBom -- then asserts lsbom reports an
// identical manifest for both. This is the functional-equivalence bar for the
// mkbom replacement.
func TestWriteBomMatchesMkbom(t *testing.T) {
for _, tool := range []string{"mkbom", "lsbom"} {
if _, err := exec.LookPath(tool); err != nil {
t.Skipf("%s not available", tool)
}
}
root := t.TempDir()
// A representative tree: nested dirs, an empty file, a binary-ish file, a
// name with a space, and varied permissions.
writeFile(t, filepath.Join(root, "opt", "orbit", "secret.txt"), []byte("SUPERSECRET"), 0o600)
writeFile(t, filepath.Join(root, "opt", "orbit", "osquery.flags"), []byte{}, 0o600)
writeFile(t, filepath.Join(root, "opt", "orbit", "bin", "orbit"), []byte("\x7fELF binary-ish payload"), 0o755)
writeFile(t, filepath.Join(root, "Library", "LaunchDaemons", "com.fleetdm.orbit.plist"), []byte("<plist/>\n"), 0o644)
writeFile(t, filepath.Join(root, "opt", "orbit", "bin", "desktop", "Fleet Desktop.app", "Contents", "Info.plist"), []byte("<x/>"), 0o644)
// Reference BOM via the native pipeline (mirrors xarBom's darwin branch).
refBom := filepath.Join(root, "..", "ref.bom")
inBom := filepath.Join(t.TempDir(), "inBom")
require.NoError(t, exec.Command("mkbom", root, inBom).Run()) //nolint:gosec
lsOut, err := exec.Command("lsbom", inBom).Output() //nolint:gosec
require.NoError(t, err)
// Rewrite ownership to root/admin (0/80), as the old darwin pipeline did.
transformed := regexp.MustCompile(`(.+)\t([0-9]+/[0-9]+)`).ReplaceAll(lsOut, []byte("$1\t0/80"))
require.NoError(t, os.WriteFile(inBom, transformed, 0o644))
cmd := exec.Command("mkbom", "-i", inBom, refBom) //nolint:gosec
require.NoError(t, cmd.Run())
// Pure-Go BOM.
myBom := filepath.Join(t.TempDir(), "my.bom")
require.NoError(t, writeBom(root, myBom))
require.Equal(t, sortedLsbom(t, refBom), sortedLsbom(t, myBom),
"lsbom manifest of writeBom output must match the native mkbom pipeline")
}
// TestWriteBomRejectsSymlink verifies writeBom fails loudly on a symlink rather
// than emitting a malformed BOM entry. Symlinks cannot legitimately appear in a
// fleetd payload (extractTarGz rejects them; the orbit "current" symlink is
// created by postinstall at install time), so this is a defensive guard.
func TestWriteBomRejectsSymlink(t *testing.T) {
root := t.TempDir()
writeFile(t, filepath.Join(root, "real.txt"), []byte("hi"), 0o644)
require.NoError(t, os.Symlink("real.txt", filepath.Join(root, "link.txt")))
err := writeBom(root, filepath.Join(t.TempDir(), "out.bom"))
require.Error(t, err)
require.Contains(t, err.Error(), "unsupported file type")
}
func writeFile(t *testing.T, path string, data []byte, mode os.FileMode) {
t.Helper()
require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755))
require.NoError(t, os.WriteFile(path, data, mode))
}
func sortedLsbom(t *testing.T, bom string) string {
t.Helper()
out, err := exec.Command("lsbom", bom).Output() //nolint:gosec
require.NoErrorf(t, err, "lsbom failed to read %s", bom)
lines := strings.Split(strings.TrimSpace(string(out)), "\n")
sort.Strings(lines)
return strings.Join(lines, "\n")
}