diff --git a/Makefile b/Makefile index 1c26f0451..4a9acb0b4 100644 --- a/Makefile +++ b/Makefile @@ -48,11 +48,10 @@ staticcheck: ## Run staticcheck.io checks ./tool/go run honnef.co/go/tools/cmd/staticcheck -- $$(./tool/go list ./... | grep -v tempfork) spk: ## Build synology package for ${SYNO_ARCH} architecture and ${SYNO_DSM} DSM version - PATH="${PWD}/tool:${PATH}" ./tool/go run github.com/tailscale/tailscale-synology@main -o tailscale.spk --source=. --goarch=${SYNO_ARCH} --dsm-version=${SYNO_DSM} + ./tool/go run ./cmd/dist build synology/dsm${SYNO_DSM}/${SYNO_ARCH} spkall: ## Build synology packages for all architectures and DSM versions - mkdir -p spks - PATH="${PWD}/tool:${PATH}" ./tool/go run github.com/tailscale/tailscale-synology@main -o spks --source=. --goarch=all --dsm-version=all + ./tool/go run ./cmd/dist build synology pushspk: spk ## Push and install synology package on ${SYNO_HOST} host echo "Pushing SPK to root@${SYNO_HOST} (env var SYNO_HOST) ..." diff --git a/cmd/dist/dist.go b/cmd/dist/dist.go index 04d1c57a9..c184e6700 100644 --- a/cmd/dist/dist.go +++ b/cmd/dist/dist.go @@ -13,15 +13,38 @@ import ( "tailscale.com/release/dist" "tailscale.com/release/dist/cli" + "tailscale.com/release/dist/synology" "tailscale.com/release/dist/unixpkgs" ) +var synologyPackageCenter bool + func getTargets() ([]dist.Target, error) { - return unixpkgs.Targets(), nil + var ret []dist.Target + + ret = append(ret, unixpkgs.Targets()...) + // Synology packages can be built either for sideloading, or for + // distribution by Synology in their package center. When + // distributed through the package center, apps can request + // additional permissions to use a tuntap interface and control + // the NAS's network stack, rather than be forced to run in + // userspace mode. + // + // Since only we can provide packages to Synology for + // distribution, we default to building the "sideload" variant of + // packages that we distribute on pkgs.tailscale.com. + ret = append(ret, synology.Targets(synologyPackageCenter)...) + return ret, nil } func main() { cmd := cli.CLI(getTargets) + for _, subcmd := range cmd.Subcommands { + if subcmd.Name == "build" { + subcmd.FlagSet.BoolVar(&synologyPackageCenter, "synology-package-center", false, "build synology packages with extra metadata for the official package center") + } + } + if err := cmd.ParseAndRun(context.Background(), os.Args[1:]); err != nil && !errors.Is(err, flag.ErrHelp) { log.Fatal(err) } diff --git a/release/dist/dist.go b/release/dist/dist.go index e3fe848a9..fcd34609c 100644 --- a/release/dist/dist.go +++ b/release/dist/dist.go @@ -17,6 +17,7 @@ import ( "sort" "strings" "sync" + "time" "tailscale.com/util/multierr" "tailscale.com/version/mkversion" @@ -44,6 +45,8 @@ type Build struct { Go string // Version is the version info of the build. Version mkversion.VersionInfo + // Time is the timestamp of the build. + Time time.Time // once is a cache of function invocations that should run once per process // (for example building a helper docker container) @@ -86,6 +89,7 @@ func NewBuild(repo, out string) (*Build, error) { Out: out, Go: goTool, Version: mkversion.Info(), + Time: time.Now().UTC(), extra: map[any]any{}, goBuildLimit: make(chan struct{}, runtime.NumCPU()), } @@ -114,6 +118,9 @@ func (b *Build) Build(targets []Target) (files []string, err error) { go func(i int, t Target) { var err error defer func() { + if err != nil { + err = fmt.Errorf("%s: %w", t, err) + } errs[i] = err wg.Done() }() diff --git a/release/dist/synology/files/PACKAGE_ICON.PNG b/release/dist/synology/files/PACKAGE_ICON.PNG new file mode 100644 index 000000000..67b09b5d2 Binary files /dev/null and b/release/dist/synology/files/PACKAGE_ICON.PNG differ diff --git a/release/dist/synology/files/PACKAGE_ICON_256.PNG b/release/dist/synology/files/PACKAGE_ICON_256.PNG new file mode 100644 index 000000000..927b5f8f4 Binary files /dev/null and b/release/dist/synology/files/PACKAGE_ICON_256.PNG differ diff --git a/release/dist/synology/files/Tailscale.sc b/release/dist/synology/files/Tailscale.sc new file mode 100644 index 000000000..707ac6bb0 --- /dev/null +++ b/release/dist/synology/files/Tailscale.sc @@ -0,0 +1,6 @@ +[Tailscale] +title="Tailscale" +desc="Tailscale VPN" +port_forward="no" +src.ports="41641/udp" +dst.ports="41641/udp" \ No newline at end of file diff --git a/release/dist/synology/files/config b/release/dist/synology/files/config new file mode 100644 index 000000000..7bd404db2 --- /dev/null +++ b/release/dist/synology/files/config @@ -0,0 +1,12 @@ +{ + ".url": { + "SYNO.SDS.Tailscale": { + "type": "url", + "version": "1.8.3", + "title": "Tailscale", + "icon": "PACKAGE_ICON_256.PNG", + "url": "webman/3rdparty/Tailscale/", + "urlTarget": "_syno_tailscale" + } + } +} diff --git a/release/dist/synology/files/index.cgi b/release/dist/synology/files/index.cgi new file mode 100755 index 000000000..7682824ec --- /dev/null +++ b/release/dist/synology/files/index.cgi @@ -0,0 +1,2 @@ +#! /bin/sh +exec /var/packages/Tailscale/target/bin/tailscale web -cgi \ No newline at end of file diff --git a/release/dist/synology/files/logrotate-dsm6 b/release/dist/synology/files/logrotate-dsm6 new file mode 100644 index 000000000..2df64283a --- /dev/null +++ b/release/dist/synology/files/logrotate-dsm6 @@ -0,0 +1,8 @@ +/var/packages/Tailscale/etc/tailscaled.stdout.log { + size 10M + rotate 3 + missingok + copytruncate + compress + notifempty +} diff --git a/release/dist/synology/files/logrotate-dsm7 b/release/dist/synology/files/logrotate-dsm7 new file mode 100644 index 000000000..7020dc925 --- /dev/null +++ b/release/dist/synology/files/logrotate-dsm7 @@ -0,0 +1,8 @@ +/var/packages/Tailscale/var/tailscaled.stdout.log { + size 10M + rotate 3 + missingok + copytruncate + compress + notifempty +} diff --git a/release/dist/synology/files/privilege-dsm6 b/release/dist/synology/files/privilege-dsm6 new file mode 100644 index 000000000..4b6fe093a --- /dev/null +++ b/release/dist/synology/files/privilege-dsm6 @@ -0,0 +1,7 @@ +{ + "defaults":{ + "run-as": "root" + }, + "username": "tailscale", + "groupname": "tailscale" +} diff --git a/release/dist/synology/files/privilege-dsm7 b/release/dist/synology/files/privilege-dsm7 new file mode 100644 index 000000000..93a9c4f7d --- /dev/null +++ b/release/dist/synology/files/privilege-dsm7 @@ -0,0 +1,7 @@ +{ + "defaults":{ + "run-as": "package" + }, + "username": "tailscale", + "groupname": "tailscale" +} diff --git a/release/dist/synology/files/privilege-dsm7.for-package-center b/release/dist/synology/files/privilege-dsm7.for-package-center new file mode 100644 index 000000000..db1468346 --- /dev/null +++ b/release/dist/synology/files/privilege-dsm7.for-package-center @@ -0,0 +1,13 @@ +{ + "defaults":{ + "run-as": "package" + }, + "username": "tailscale", + "groupname": "tailscale", + "tool": [{ + "relpath": "bin/tailscaled", + "user": "package", + "group": "package", + "capabilities": "cap_net_admin,cap_chown,cap_net_raw" + }] +} diff --git a/release/dist/synology/files/resource b/release/dist/synology/files/resource new file mode 100644 index 000000000..0da0002ef --- /dev/null +++ b/release/dist/synology/files/resource @@ -0,0 +1,11 @@ +{ + "port-config": { + "protocol-file": "conf/Tailscale.sc" + }, + "usr-local-linker": { + "bin": ["bin/tailscale"] + }, + "syslog-config": { + "logrotate-relpath": "conf/logrotate.conf" + } +} \ No newline at end of file diff --git a/release/dist/synology/files/scripts/postupgrade b/release/dist/synology/files/scripts/postupgrade new file mode 100644 index 000000000..92b94c40c --- /dev/null +++ b/release/dist/synology/files/scripts/postupgrade @@ -0,0 +1,3 @@ +#!/bin/sh + +exit 0 \ No newline at end of file diff --git a/release/dist/synology/files/scripts/preupgrade b/release/dist/synology/files/scripts/preupgrade new file mode 100644 index 000000000..92b94c40c --- /dev/null +++ b/release/dist/synology/files/scripts/preupgrade @@ -0,0 +1,3 @@ +#!/bin/sh + +exit 0 \ No newline at end of file diff --git a/release/dist/synology/files/scripts/start-stop-status b/release/dist/synology/files/scripts/start-stop-status new file mode 100755 index 000000000..e6ece04e3 --- /dev/null +++ b/release/dist/synology/files/scripts/start-stop-status @@ -0,0 +1,129 @@ +#!/bin/bash + +SERVICE_NAME="tailscale" + +if [ "${SYNOPKG_DSM_VERSION_MAJOR}" -eq "6" ]; then + PKGVAR="/var/packages/Tailscale/etc" +else + PKGVAR="${SYNOPKG_PKGVAR}" +fi + +PID_FILE="${PKGVAR}/tailscaled.pid" +LOG_FILE="${PKGVAR}/tailscaled.stdout.log" +STATE_FILE="${PKGVAR}/tailscaled.state" +SOCKET_FILE="${PKGVAR}/tailscaled.sock" +PORT="41641" + +SERVICE_COMMAND="${SYNOPKG_PKGDEST}/bin/tailscaled \ +--state=${STATE_FILE} \ +--socket=${SOCKET_FILE} \ +--port=$PORT" + +if [ "${SYNOPKG_DSM_VERSION_MAJOR}" -eq "7" -a ! -e "/dev/net/tun" ]; then + # TODO(maisem/crawshaw): Disable the tun device in DSM7 for now. + SERVICE_COMMAND="${SERVICE_COMMAND} --tun=userspace-networking" +fi + +if [ "${SYNOPKG_DSM_VERSION_MAJOR}" -eq "6" ]; then + chown -R tailscale:tailscale "${PKGVAR}/" +fi + +start_daemon() { + local ts=$(date --iso-8601=second) + echo "${ts} Starting ${SERVICE_NAME} with: ${SERVICE_COMMAND}" >${LOG_FILE} + STATE_DIRECTORY=${PKGVAR} ${SERVICE_COMMAND} 2>&1 | sed -u '1,200p;201s,.*,[further tailscaled logs suppressed],p;d' >>${LOG_FILE} & + # We pipe tailscaled's output to sed, so "$!" retrieves the PID of sed not tailscaled. + # Use jobs -p to retrieve the PID of the most recent process group leader. + jobs -p >"${PID_FILE}" +} + +stop_daemon() { + if [ -r "${PID_FILE}" ]; then + local PID=$(cat "${PID_FILE}") + local ts=$(date --iso-8601=second) + echo "${ts} Stopping ${SERVICE_NAME} service PID=${PID}" >>${LOG_FILE} + kill -TERM $PID >>${LOG_FILE} 2>&1 + wait_for_status 1 || kill -KILL $PID >>${LOG_FILE} 2>&1 + rm -f "${PID_FILE}" >/dev/null + fi +} + +daemon_status() { + if [ -r "${PID_FILE}" ]; then + local PID=$(cat "${PID_FILE}") + if ps -o pid -p ${PID} > /dev/null; then + return + fi + rm -f "${PID_FILE}" >/dev/null + fi + return 1 +} + +wait_for_status() { + # 20 tries + # sleeps for 1 second after each try + local counter=20 + while [ ${counter} -gt 0 ]; do + daemon_status + [ $? -eq $1 ] && return + counter=$((counter - 1)) + sleep 1 + done + return 1 +} + +ensure_tun_created() { + if [ "${SYNOPKG_DSM_VERSION_MAJOR}" -eq "7" ]; then + # TODO(maisem/crawshaw): Disable the tun device in DSM7 for now. + return + fi + # Create the necessary file structure for /dev/net/tun + if ([ ! -c /dev/net/tun ]); then + if ([ ! -d /dev/net ]); then + mkdir -m 755 /dev/net + fi + mknod /dev/net/tun c 10 200 + chmod 0755 /dev/net/tun + fi + + # Load the tun module if not already loaded + if (!(lsmod | grep -q "^tun\s")); then + insmod /lib/modules/tun.ko + fi +} + +case $1 in +start) + if daemon_status; then + exit 0 + else + ensure_tun_created + start_daemon + exit $? + fi + ;; +stop) + if daemon_status; then + stop_daemon + exit $? + else + exit 0 + fi + ;; +status) + if daemon_status; then + echo "${SERVICE_NAME} is running" + exit 0 + else + echo "${SERVICE_NAME} is not running" + exit 3 + fi + ;; +log) + exit 0 + ;; +*) + echo "command $1 is not implemented" + exit 0 + ;; +esac diff --git a/release/dist/synology/pkgs.go b/release/dist/synology/pkgs.go new file mode 100644 index 000000000..86d83d84b --- /dev/null +++ b/release/dist/synology/pkgs.go @@ -0,0 +1,306 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +// Package synology contains dist Targets for building Synology Tailscale packages. +package synology + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "embed" + "fmt" + "io" + "io/fs" + "log" + "os" + "path/filepath" + "time" + + "tailscale.com/release/dist" +) + +type target struct { + filenameArch string + dsmMajorVersion int + goenv map[string]string + packageCenter bool +} + +func (t *target) String() string { + return fmt.Sprintf("synology/dsm%d/%s", t.dsmMajorVersion, t.filenameArch) +} + +func (t *target) Build(b *dist.Build) ([]string, error) { + inner, err := getSynologyBuilds(b).buildInnerPackage(b, t.dsmMajorVersion, t.goenv) + if err != nil { + return nil, err + } + + out, err := t.buildSPK(b, inner) + if err != nil { + return nil, err + } + + return []string{out}, nil +} + +func (t *target) buildSPK(b *dist.Build, inner *innerPkg) (string, error) { + filename := fmt.Sprintf("tailscale-%s-%s-%d-dsm%d.spk", t.filenameArch, b.Version.Short, b.Version.Synology[t.dsmMajorVersion], t.dsmMajorVersion) + out := filepath.Join(b.Out, filename) + log.Printf("Building %s", filename) + + privFile := fmt.Sprintf("privilege-dsm%d", t.dsmMajorVersion) + if t.packageCenter && t.dsmMajorVersion == 7 { + privFile += ".for-package-center" + } + + f, err := os.Create(out) + if err != nil { + return "", err + } + defer f.Close() + tw := tar.NewWriter(f) + defer tw.Close() + + err = writeTar(tw, b.Time, + memFile("INFO", t.mkInfo(b, inner.uncompressedSz), 0644), + static("PACKAGE_ICON.PNG", "PACKAGE_ICON.PNG", 0644), + static("PACKAGE_ICON_256.PNG", "PACKAGE_ICON_256.PNG", 0644), + static("Tailscale.sc", "Tailscale.sc", 0644), + dir("conf"), + static("resource", "conf/resource", 0644), + static(privFile, "conf/privilege", 0644), + file(inner.path, "package.tgz", 0644), + dir("scripts"), + static("scripts/start-stop-status", "scripts/start-stop-status", 0644), + static("scripts/postupgrade", "scripts/postupgrade", 0644), + static("scripts/preupgrade", "scripts/preupgrade", 0644), + ) + if err != nil { + return "", err + } + + if err := tw.Close(); err != nil { + return "", err + } + if err := f.Close(); err != nil { + return "", err + } + + return out, nil +} + +func (t *target) mkInfo(b *dist.Build, uncompressedSz int64) []byte { + var ret bytes.Buffer + f := func(k, v string) { + fmt.Fprintf(&ret, "%s=%q\n", k, v) + } + f("package", "Tailscale") + f("version", fmt.Sprintf("%s-%d", b.Version.Short, b.Version.Synology[t.dsmMajorVersion])) + f("arch", t.filenameArch) + f("description", "Connect all your devices using WireGuard, without the hassle.") + f("displayname", "Tailscale") + f("maintainer", "Tailscale, Inc.") + f("maintainer_url", "https://github.com/tailscale/tailscale") + f("create_time", b.Time.Format("20060102-15:04:05")) + f("dsmuidir", "ui") + f("dsmappname", "SYNO.SDS.Tailscale") + f("startstop_restart_services", "nginx") + switch t.dsmMajorVersion { + case 6: + f("os_min_ver", "6.0.1-7445") + f("os_max_ver", "7.0-40000") + case 7: + f("os_min_ver", "7.0-40000") + f("os_max_ver", "") + default: + panic(fmt.Sprintf("unsupported DSM major version %d", t.dsmMajorVersion)) + } + f("extractsize", fmt.Sprintf("%v", uncompressedSz>>10)) // in KiB + return ret.Bytes() +} + +type synologyBuildsMemoizeKey struct{} + +type innerPkg struct { + path string + uncompressedSz int64 +} + +// synologyBuilds is extra build context shared by all synology builds. +type synologyBuilds struct { + innerPkgs dist.Memoize[*innerPkg] +} + +// getSynologyBuilds returns the synologyBuilds for b, creating one if needed. +func getSynologyBuilds(b *dist.Build) *synologyBuilds { + return b.Extra(synologyBuildsMemoizeKey{}, func() any { return new(synologyBuilds) }).(*synologyBuilds) +} + +// buildInnerPackage builds the inner tarball for synology packages, +// which contains the files to unpack to disk on installation (as +// opposed to the outer tarball, which contains package metadata) +func (m *synologyBuilds) buildInnerPackage(b *dist.Build, dsmVersion int, goenv map[string]string) (*innerPkg, error) { + key := []any{dsmVersion, goenv} + return m.innerPkgs.Do(key, func() (*innerPkg, error) { + ts, err := b.BuildGoBinary("tailscale.com/cmd/tailscale", goenv) + if err != nil { + return nil, err + } + tsd, err := b.BuildGoBinary("tailscale.com/cmd/tailscaled", goenv) + if err != nil { + return nil, err + } + + tmp := b.TmpDir() + out := filepath.Join(tmp, "package.tgz") + + f, err := os.Create(out) + if err != nil { + return nil, err + } + defer f.Close() + gw := gzip.NewWriter(f) + defer gw.Close() + cw := &countingWriter{gw, 0} + tw := tar.NewWriter(cw) + defer tw.Close() + + err = writeTar(tw, b.Time, + dir("bin"), + file(tsd, "bin/tailscaled", 0755), + file(ts, "bin/tailscale", 0755), + dir("conf"), + static("Tailscale.sc", "conf/Tailscale.sc", 0644), + static(fmt.Sprintf("logrotate-dsm%d", dsmVersion), "conf/logrotate.conf", 0644), + dir("ui"), + static("PACKAGE_ICON_256.PNG", "ui/PACKAGE_ICON_256.PNG", 0644), + static("config", "ui/config", 0644), // TODO: this has "1.8.3" hard-coded in it; why? what is it? bug? + static("index.cgi", "ui/index.cgi", 0755)) + if err != nil { + return nil, err + } + + if err := tw.Close(); err != nil { + return nil, err + } + if err := gw.Close(); err != nil { + return nil, err + } + if err := f.Close(); err != nil { + return nil, err + } + + return &innerPkg{out, cw.n}, nil + }) +} + +// writeTar writes ents to tw. +func writeTar(tw *tar.Writer, modTime time.Time, ents ...tarEntry) error { + for _, ent := range ents { + if err := ent(tw, modTime); err != nil { + return err + } + } + return nil +} + +// tarEntry is a function that writes tar entries (files or +// directories) to a tar.Writer. +type tarEntry func(*tar.Writer, time.Time) error + +// fsFile returns a tarEntry that writes src in fsys to dst in the tar +// file, with mode. +func fsFile(fsys fs.FS, src, dst string, mode int64) tarEntry { + return func(tw *tar.Writer, modTime time.Time) error { + f, err := fsys.Open(src) + if err != nil { + return err + } + defer f.Close() + fi, err := f.Stat() + if err != nil { + return err + } + hdr := &tar.Header{ + Name: dst, + Size: fi.Size(), + Mode: mode, + ModTime: modTime, + } + if err := tw.WriteHeader(hdr); err != nil { + return err + } + if _, err = io.Copy(tw, f); err != nil { + return err + } + return nil + } +} + +// file returns a tarEntry that writes src on disk into the tar file as +// dst, with mode. +func file(src, dst string, mode int64) tarEntry { + return fsFile(os.DirFS(filepath.Dir(src)), filepath.Base(src), dst, mode) +} + +//go:embed files/* +var files embed.FS + +// static returns a tarEntry that writes src in files/ into the tar +// file as dst, with mode. +func static(src, dst string, mode int64) tarEntry { + fsys, err := fs.Sub(files, "files") + if err != nil { + panic(err) + } + return fsFile(fsys, src, dst, mode) +} + +// memFile returns a tarEntry that writes bs to dst in the tar file, +// with mode. +func memFile(dst string, bs []byte, mode int64) tarEntry { + return func(tw *tar.Writer, modTime time.Time) error { + hdr := &tar.Header{ + Name: dst, + Size: int64(len(bs)), + Mode: mode, + ModTime: modTime, + } + if err := tw.WriteHeader(hdr); err != nil { + return err + } + if _, err := tw.Write(bs); err != nil { + return err + } + return nil + } +} + +// dir returns a tarEntry that creates a world-readable directory in +// the tar file. +func dir(name string) tarEntry { + return func(tw *tar.Writer, modTime time.Time) error { + return tw.WriteHeader(&tar.Header{ + Typeflag: tar.TypeDir, + Name: name + "/", + Mode: 0755, + ModTime: modTime, + // TODO: why tailscale? Files are being written as owned by root. + Uname: "tailscale", + Gname: "tailscale", + }) + } +} + +type countingWriter struct { + w io.Writer + n int64 +} + +func (cw *countingWriter) Write(bs []byte) (int, error) { + n, err := cw.w.Write(bs) + cw.n += int64(n) + return n, err +} diff --git a/release/dist/synology/targets.go b/release/dist/synology/targets.go new file mode 100644 index 000000000..3ef346c1e --- /dev/null +++ b/release/dist/synology/targets.go @@ -0,0 +1,69 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +package synology + +import "tailscale.com/release/dist" + +func Targets(forPackageCenter bool) []dist.Target { + var ret []dist.Target + for _, dsmVersion := range []int{6, 7} { + ret = append(ret, + &target{ + filenameArch: "x86_64", + dsmMajorVersion: dsmVersion, + goenv: map[string]string{ + "GOOS": "linux", + "GOARCH": "amd64", + }, + packageCenter: forPackageCenter, + }, + &target{ + filenameArch: "i686", + dsmMajorVersion: dsmVersion, + goenv: map[string]string{ + "GOOS": "linux", + "GOARCH": "386", + }, + packageCenter: forPackageCenter, + }, + &target{ + filenameArch: "armv8", + dsmMajorVersion: dsmVersion, + goenv: map[string]string{ + "GOOS": "linux", + "GOARCH": "arm64", + }, + packageCenter: forPackageCenter, + }) + + // On older ARMv5 and ARMv7 platforms, synology used a whole + // mess of SoC-specific target names, even though the packages + // built for each are identical apart from metadata. + for _, v5Arch := range []string{"armv5", "88f6281", "88f6282"} { + ret = append(ret, &target{ + filenameArch: v5Arch, + dsmMajorVersion: dsmVersion, + goenv: map[string]string{ + "GOOS": "linux", + "GOARCH": "arm", + "GOARM": "5", + }, + packageCenter: forPackageCenter, + }) + } + for _, v7Arch := range []string{"armv7", "alpine", "armada370", "armada375", "armada38x", "armadaxp", "comcerto2k", "monaco", "hi3535"} { + ret = append(ret, &target{ + filenameArch: v7Arch, + dsmMajorVersion: dsmVersion, + goenv: map[string]string{ + "GOOS": "linux", + "GOARCH": "arm", + "GOARM": "7", + }, + packageCenter: forPackageCenter, + }) + } + } + return ret +} diff --git a/release/dist/unixpkgs/pkgs.go b/release/dist/unixpkgs/pkgs.go index 61a6235df..f8a32fc3d 100644 --- a/release/dist/unixpkgs/pkgs.go +++ b/release/dist/unixpkgs/pkgs.go @@ -14,7 +14,6 @@ import ( "os" "path/filepath" "strings" - "time" "github.com/goreleaser/nfpm" "tailscale.com/release/dist" @@ -71,7 +70,6 @@ func (t *tgzTarget) Build(b *dist.Build) ([]string, error) { tw := tar.NewWriter(gw) defer tw.Close() - buildTime := time.Now() addFile := func(src, dst string, mode int64) error { f, err := os.Open(src) if err != nil { @@ -86,7 +84,7 @@ func (t *tgzTarget) Build(b *dist.Build) ([]string, error) { Name: dst, Size: fi.Size(), Mode: mode, - ModTime: buildTime, + ModTime: b.Time, Uid: 0, Gid: 0, Uname: "root", @@ -104,7 +102,7 @@ func (t *tgzTarget) Build(b *dist.Build) ([]string, error) { hdr := &tar.Header{ Name: name + "/", Mode: 0755, - ModTime: buildTime, + ModTime: b.Time, Uid: 0, Gid: 0, Uname: "root", diff --git a/tool/gocross/autoflags.go b/tool/gocross/autoflags.go index 0fd46c4fa..f51d20671 100644 --- a/tool/gocross/autoflags.go +++ b/tool/gocross/autoflags.go @@ -153,7 +153,9 @@ func autoflagsForTest(argv []string, env *Environment, goroot, nativeGOOS, nativ env.Set("GOOS", targetOS) env.Set("GOARCH", targetArch) - env.Set("GOARM", "5") // TODO: fix, see go/internal-bug/3092 + if !env.IsSet("GOARM") { + env.Set("GOARM", "5") // TODO: fix, see go/internal-bug/3092 + } env.Set("GOMIPS", "softfloat") env.Set("CGO_ENABLED", boolStr(cgo)) env.Set("CGO_CFLAGS", strings.Join(cgoCflags, " ")) diff --git a/version/mkversion/mkversion.go b/version/mkversion/mkversion.go index af5244e91..a7d60f528 100644 --- a/version/mkversion/mkversion.go +++ b/version/mkversion/mkversion.go @@ -61,6 +61,10 @@ type VersionInfo struct { // Winres is the version string that gets embedded into Windows exe // metadata. It is of the form "x,y,z,0". Winres string + // Synology is a map of Synology DSM major version to the + // Tailscale numeric version that gets embedded in Synology spk + // files. + Synology map[int]int64 // GitDate is the unix timestamp of GitHash's commit date. GitDate string // OtherDate is the unix timestamp of OtherHash's commit date, if any. @@ -239,6 +243,10 @@ func mkOutput(v verInfo) (VersionInfo, error) { GitHash: fmt.Sprintf("%s", v.hash), GitDate: fmt.Sprintf("%s", v.date), Track: track, + Synology: map[int]int64{ + 6: 6*1_000_000_000 + int64(v.major-1)*1_000_000 + int64(v.minor)*1_000 + int64(v.patch), + 7: 7*1_000_000_000 + int64(v.major-1)*1_000_000 + int64(v.minor)*1_000 + int64(v.patch), + }, } if v.otherHash != "" {