diff --git a/Dockerfile.tailscale-HEAD b/Dockerfile.tailscale-HEAD index 96edf72c..67849602 100644 --- a/Dockerfile.tailscale-HEAD +++ b/Dockerfile.tailscale-HEAD @@ -18,6 +18,11 @@ RUN git clone https://github.com/tailscale/tailscale.git WORKDIR /go/src/tailscale +# Optional: checkout a specific ref (tag, branch, or commit hash). +# When empty, builds from HEAD of the default branch. +ARG TAILSCALE_REF="" +RUN if [ -n "$TAILSCALE_REF" ]; then git checkout "$TAILSCALE_REF"; fi + # see build_docker.sh ARG VERSION_LONG="" diff --git a/integration/tsic/tsic.go b/integration/tsic/tsic.go index fb07896b..79a3bc06 100644 --- a/integration/tsic/tsic.go +++ b/integration/tsic/tsic.go @@ -105,6 +105,7 @@ type TailscaleInContainer struct { type TailscaleInContainerBuildConfig struct { tags []string + ref string // git ref (tag, branch, commit) to checkout; empty means HEAD } // Option represent optional settings that can be given to a @@ -205,6 +206,19 @@ func WithBuildTag(tag string) Option { } } +// WithTailscaleRef sets the git ref (tag, branch, or commit hash) to checkout +// when building the Tailscale client from source. This option is only meaningful +// when invoked on HEAD versions of the client. +func WithTailscaleRef(ref string) Option { + return func(tsic *TailscaleInContainer) { + if tsic.version != VersionHead { + panic(errInvalidClientConfig) + } + + tsic.buildConfig.ref = ref + } +} + // WithExtraLoginArgs adds additional arguments to the `tailscale up` command // as part of the Login function. func WithExtraLoginArgs(args []string) Option { @@ -398,15 +412,23 @@ func New( // Check if a pre-built image is available via environment variable prebuiltImage := os.Getenv("HEADSCALE_INTEGRATION_TAILSCALE_IMAGE") - // If custom build tags are required (e.g., for websocket DERP), we cannot use + // If custom build tags or a specific ref are required, we cannot use // the pre-built image as it won't have the necessary code compiled in. hasBuildTags := len(tsic.buildConfig.tags) > 0 + + hasRef := tsic.buildConfig.ref != "" if hasBuildTags && prebuiltImage != "" { log.Printf("Ignoring pre-built image %s because custom build tags are required: %v", prebuiltImage, tsic.buildConfig.tags) prebuiltImage = "" } + if hasRef && prebuiltImage != "" { + log.Printf("Ignoring pre-built image %s because a specific ref is required: %s", + prebuiltImage, tsic.buildConfig.ref) + prebuiltImage = "" + } + if prebuiltImage != "" { log.Printf("Using pre-built tailscale image: %s", prebuiltImage) @@ -429,8 +451,8 @@ func New( if err != nil { return nil, fmt.Errorf("could not run pre-built tailscale container %q: %w", prebuiltImage, err) } - } else if util.IsCI() && !hasBuildTags { - // In CI, we require a pre-built image unless custom build tags are needed + } else if util.IsCI() && !hasBuildTags && !hasRef { + // In CI, we require a pre-built image unless custom build tags or ref are needed return nil, errTailscaleImageRequiredInCI } else { buildOptions := &dockertest.BuildOptions{ @@ -450,6 +472,16 @@ func New( ) } + if tsic.buildConfig.ref != "" { + buildOptions.BuildArgs = append( + buildOptions.BuildArgs, + docker.BuildArg{ + Name: "TAILSCALE_REF", + Value: tsic.buildConfig.ref, + }, + ) + } + container, err = pool.BuildAndRunWithBuildOptions( buildOptions, tailscaleOptions,