test.yml 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. # This is our main "CI tests" workflow. It runs everything that should run on
  2. # both PRs and merged commits, and for the latter reports failures to slack.
  3. name: CI
  4. env:
  5. # Our fuzz job, powered by OSS-Fuzz, fails periodically because we upgrade to
  6. # new Go versions very eagerly. OSS-Fuzz is a little more conservative, and
  7. # ends up being unable to compile our code.
  8. #
  9. # When this happens, we want to disable the fuzz target until OSS-Fuzz catches
  10. # up. However, we also don't want to forget to turn it back on when OSS-Fuzz
  11. # can once again build our code.
  12. #
  13. # This variable toggles the fuzz job between two modes:
  14. # - false: we expect fuzzing to be happy, and should report failure if it's not.
  15. # - true: we expect fuzzing is broken, and should report failure if it start working.
  16. TS_FUZZ_CURRENTLY_BROKEN: false
  17. on:
  18. push:
  19. branches:
  20. - "main"
  21. - "release-branch/*"
  22. pull_request:
  23. # all PRs on all branches
  24. merge_group:
  25. branches:
  26. - "main"
  27. concurrency:
  28. # For PRs, later CI runs preempt previous ones. e.g. a force push on a PR
  29. # cancels running CI jobs and starts all new ones.
  30. #
  31. # For non-PR pushes, concurrency.group needs to be unique for every distinct
  32. # CI run we want to have happen. Use run_id, which in practice means all
  33. # non-PR CI runs will be allowed to run without preempting each other.
  34. group: ${{ github.workflow }}-$${{ github.pull_request.number || github.run_id }}
  35. cancel-in-progress: true
  36. jobs:
  37. race-root-integration:
  38. runs-on: ubuntu-22.04
  39. strategy:
  40. fail-fast: false # don't abort the entire matrix if one element fails
  41. matrix:
  42. include:
  43. - shard: '1/4'
  44. - shard: '2/4'
  45. - shard: '3/4'
  46. - shard: '4/4'
  47. steps:
  48. - name: checkout
  49. uses: actions/checkout@v4
  50. - name: build test wrapper
  51. run: ./tool/go build -o /tmp/testwrapper ./cmd/testwrapper
  52. - name: integration tests as root
  53. run: PATH=$PWD/tool:$PATH /tmp/testwrapper -exec "sudo -E" -race ./tstest/integration/
  54. env:
  55. TS_TEST_SHARD: ${{ matrix.shard }}
  56. test:
  57. strategy:
  58. fail-fast: false # don't abort the entire matrix if one element fails
  59. matrix:
  60. include:
  61. - goarch: amd64
  62. coverflags: "-coverprofile=/tmp/coverage.out"
  63. - goarch: amd64
  64. buildflags: "-race"
  65. shard: '1/3'
  66. - goarch: amd64
  67. buildflags: "-race"
  68. shard: '2/3'
  69. - goarch: amd64
  70. buildflags: "-race"
  71. shard: '3/3'
  72. - goarch: "386" # thanks yaml
  73. runs-on: ubuntu-22.04
  74. steps:
  75. - name: checkout
  76. uses: actions/checkout@v4
  77. - name: Restore Cache
  78. uses: actions/cache@v3
  79. with:
  80. # Note: unlike the other setups, this is only grabbing the mod download
  81. # cache, rather than the whole mod directory, as the download cache
  82. # contains zips that can be unpacked in parallel faster than they can be
  83. # fetched and extracted by tar
  84. path: |
  85. ~/.cache/go-build
  86. ~/go/pkg/mod/cache
  87. ~\AppData\Local\go-build
  88. # The -2- here should be incremented when the scheme of data to be
  89. # cached changes (e.g. path above changes).
  90. key: ${{ github.job }}-${{ runner.os }}-${{ matrix.goarch }}-${{ matrix.buildflags }}-go-2-${{ hashFiles('**/go.sum') }}-${{ github.run_id }}
  91. restore-keys: |
  92. ${{ github.job }}-${{ runner.os }}-${{ matrix.goarch }}-${{ matrix.buildflags }}-go-2-${{ hashFiles('**/go.sum') }}
  93. ${{ github.job }}-${{ runner.os }}-${{ matrix.goarch }}-${{ matrix.buildflags }}-go-2-
  94. - name: build all
  95. if: matrix.buildflags == '' # skip on race builder
  96. run: ./tool/go build ${{matrix.buildflags}} ./...
  97. env:
  98. GOARCH: ${{ matrix.goarch }}
  99. - name: build variant CLIs
  100. if: matrix.buildflags == '' # skip on race builder
  101. run: |
  102. export TS_USE_TOOLCHAIN=1
  103. ./build_dist.sh --extra-small ./cmd/tailscaled
  104. ./build_dist.sh --box ./cmd/tailscaled
  105. ./build_dist.sh --extra-small --box ./cmd/tailscaled
  106. rm -f tailscaled
  107. env:
  108. GOARCH: ${{ matrix.goarch }}
  109. - name: get qemu # for tstest/archtest
  110. if: matrix.goarch == 'amd64' && matrix.buildflags == ''
  111. run: |
  112. sudo apt-get -y update
  113. sudo apt-get -y install qemu-user
  114. - name: build test wrapper
  115. run: ./tool/go build -o /tmp/testwrapper ./cmd/testwrapper
  116. - name: test all
  117. run: NOBASHDEBUG=true PATH=$PWD/tool:$PATH /tmp/testwrapper ${{matrix.coverflags}} ./... ${{matrix.buildflags}}
  118. env:
  119. GOARCH: ${{ matrix.goarch }}
  120. TS_TEST_SHARD: ${{ matrix.shard }}
  121. - name: Publish to coveralls.io
  122. if: matrix.coverflags != '' # only publish results if we've tracked coverage
  123. uses: shogo82148/actions-goveralls@v1
  124. with:
  125. path-to-profile: /tmp/coverage.out
  126. - name: bench all
  127. run: ./tool/go test ${{matrix.buildflags}} -bench=. -benchtime=1x -run=^$ $(for x in $(git grep -l "^func Benchmark" | xargs dirname | sort | uniq); do echo "./$x"; done)
  128. env:
  129. GOARCH: ${{ matrix.goarch }}
  130. - name: check that no tracked files changed
  131. run: git diff --no-ext-diff --name-only --exit-code || (echo "Build/test modified the files above."; exit 1)
  132. - name: check that no new files were added
  133. run: |
  134. # Note: The "error: pathspec..." you see below is normal!
  135. # In the success case in which there are no new untracked files,
  136. # git ls-files complains about the pathspec not matching anything.
  137. # That's OK. It's not worth the effort to suppress. Please ignore it.
  138. if git ls-files --others --exclude-standard --directory --no-empty-directory --error-unmatch -- ':/*'
  139. then
  140. echo "Build/test created untracked files in the repo (file names above)."
  141. exit 1
  142. fi
  143. windows:
  144. runs-on: windows-2022
  145. steps:
  146. - name: checkout
  147. uses: actions/checkout@v4
  148. - name: Install Go
  149. uses: actions/setup-go@v4
  150. with:
  151. go-version-file: go.mod
  152. cache: false
  153. - name: Restore Cache
  154. uses: actions/cache@v3
  155. with:
  156. # Note: unlike the other setups, this is only grabbing the mod download
  157. # cache, rather than the whole mod directory, as the download cache
  158. # contains zips that can be unpacked in parallel faster than they can be
  159. # fetched and extracted by tar
  160. path: |
  161. ~/.cache/go-build
  162. ~/go/pkg/mod/cache
  163. ~\AppData\Local\go-build
  164. # The -2- here should be incremented when the scheme of data to be
  165. # cached changes (e.g. path above changes).
  166. key: ${{ github.job }}-${{ runner.os }}-go-2-${{ hashFiles('**/go.sum') }}-${{ github.run_id }}
  167. restore-keys: |
  168. ${{ github.job }}-${{ runner.os }}-go-2-${{ hashFiles('**/go.sum') }}
  169. ${{ github.job }}-${{ runner.os }}-go-2-
  170. - name: test
  171. run: go run ./cmd/testwrapper ./...
  172. - name: bench all
  173. # Don't use -bench=. -benchtime=1x.
  174. # Somewhere in the layers (powershell?)
  175. # the equals signs cause great confusion.
  176. run: go test ./... -bench . -benchtime 1x -run "^$"
  177. vm:
  178. runs-on: ["self-hosted", "linux", "vm"]
  179. # VM tests run with some privileges, don't let them run on 3p PRs.
  180. if: github.repository == 'tailscale/tailscale'
  181. steps:
  182. - name: checkout
  183. uses: actions/checkout@v4
  184. - name: Run VM tests
  185. run: ./tool/go test ./tstest/integration/vms -v -no-s3 -run-vm-tests -run=TestRunUbuntu2004
  186. env:
  187. HOME: "/tmp"
  188. TMPDIR: "/tmp"
  189. XDB_CACHE_HOME: "/var/lib/ghrunner/cache"
  190. race-build:
  191. runs-on: ubuntu-22.04
  192. steps:
  193. - name: checkout
  194. uses: actions/checkout@v4
  195. - name: build all
  196. run: ./tool/go install -race ./cmd/...
  197. - name: build tests
  198. run: ./tool/go test -race -exec=true ./...
  199. cross: # cross-compile checks, build only.
  200. strategy:
  201. fail-fast: false # don't abort the entire matrix if one element fails
  202. matrix:
  203. include:
  204. # Note: linux/amd64 is not in this matrix, because that goos/goarch is
  205. # tested more exhaustively in the 'test' job above.
  206. - goos: linux
  207. goarch: arm64
  208. - goos: linux
  209. goarch: "386" # thanks yaml
  210. - goos: linux
  211. goarch: loong64
  212. - goos: linux
  213. goarch: arm
  214. goarm: "5"
  215. - goos: linux
  216. goarch: arm
  217. goarm: "7"
  218. # macOS
  219. - goos: darwin
  220. goarch: amd64
  221. - goos: darwin
  222. goarch: arm64
  223. # Windows
  224. - goos: windows
  225. goarch: amd64
  226. - goos: windows
  227. goarch: arm64
  228. # BSDs
  229. - goos: freebsd
  230. goarch: amd64
  231. - goos: openbsd
  232. goarch: amd64
  233. # Plan9 (disabled until 3p dependencies are fixed)
  234. # - goos: plan9
  235. # goarch: amd64
  236. runs-on: ubuntu-22.04
  237. steps:
  238. - name: checkout
  239. uses: actions/checkout@v4
  240. - name: Restore Cache
  241. uses: actions/cache@v3
  242. with:
  243. # Note: unlike the other setups, this is only grabbing the mod download
  244. # cache, rather than the whole mod directory, as the download cache
  245. # contains zips that can be unpacked in parallel faster than they can be
  246. # fetched and extracted by tar
  247. path: |
  248. ~/.cache/go-build
  249. ~/go/pkg/mod/cache
  250. ~\AppData\Local\go-build
  251. # The -2- here should be incremented when the scheme of data to be
  252. # cached changes (e.g. path above changes).
  253. key: ${{ github.job }}-${{ runner.os }}-${{ matrix.goos }}-${{ matrix.goarch }}-go-2-${{ hashFiles('**/go.sum') }}-${{ github.run_id }}
  254. restore-keys: |
  255. ${{ github.job }}-${{ runner.os }}-${{ matrix.goos }}-${{ matrix.goarch }}-go-2-${{ hashFiles('**/go.sum') }}
  256. ${{ github.job }}-${{ runner.os }}-${{ matrix.goos }}-${{ matrix.goarch }}-go-2-
  257. - name: build all
  258. run: ./tool/go build ./cmd/...
  259. env:
  260. GOOS: ${{ matrix.goos }}
  261. GOARCH: ${{ matrix.goarch }}
  262. GOARM: ${{ matrix.goarm }}
  263. CGO_ENABLED: "0"
  264. - name: build tests
  265. run: ./tool/go test -exec=true ./...
  266. env:
  267. GOOS: ${{ matrix.goos }}
  268. GOARCH: ${{ matrix.goarch }}
  269. CGO_ENABLED: "0"
  270. ios: # similar to cross above, but iOS can't build most of the repo. So, just
  271. #make it build a few smoke packages.
  272. runs-on: ubuntu-22.04
  273. steps:
  274. - name: checkout
  275. uses: actions/checkout@v4
  276. - name: build some
  277. run: ./tool/go build ./ipn/... ./wgengine/ ./types/... ./control/controlclient
  278. env:
  279. GOOS: ios
  280. GOARCH: arm64
  281. android:
  282. # similar to cross above, but android fails to build a few pieces of the
  283. # repo. We should fix those pieces, they're small, but as a stepping stone,
  284. # only test the subset of android that our past smoke test checked.
  285. runs-on: ubuntu-22.04
  286. steps:
  287. - name: checkout
  288. uses: actions/checkout@v4
  289. # Super minimal Android build that doesn't even use CGO and doesn't build everything that's needed
  290. # and is only arm64. But it's a smoke build: it's not meant to catch everything. But it'll catch
  291. # some Android breakages early.
  292. # TODO(bradfitz): better; see https://github.com/tailscale/tailscale/issues/4482
  293. - name: build some
  294. run: ./tool/go install ./net/netns ./ipn/ipnlocal ./wgengine/magicsock/ ./wgengine/ ./wgengine/router/ ./wgengine/netstack ./util/dnsname/ ./ipn/ ./net/interfaces ./wgengine/router/ ./tailcfg/ ./types/logger/ ./net/dns ./hostinfo ./version
  295. env:
  296. GOOS: android
  297. GOARCH: arm64
  298. wasm: # builds tsconnect, which is the only wasm build we support
  299. runs-on: ubuntu-22.04
  300. steps:
  301. - name: checkout
  302. uses: actions/checkout@v4
  303. - name: Restore Cache
  304. uses: actions/cache@v3
  305. with:
  306. # Note: unlike the other setups, this is only grabbing the mod download
  307. # cache, rather than the whole mod directory, as the download cache
  308. # contains zips that can be unpacked in parallel faster than they can be
  309. # fetched and extracted by tar
  310. path: |
  311. ~/.cache/go-build
  312. ~/go/pkg/mod/cache
  313. ~\AppData\Local\go-build
  314. # The -2- here should be incremented when the scheme of data to be
  315. # cached changes (e.g. path above changes).
  316. key: ${{ github.job }}-${{ runner.os }}-go-2-${{ hashFiles('**/go.sum') }}-${{ github.run_id }}
  317. restore-keys: |
  318. ${{ github.job }}-${{ runner.os }}-go-2-${{ hashFiles('**/go.sum') }}
  319. ${{ github.job }}-${{ runner.os }}-go-2-
  320. - name: build tsconnect client
  321. run: ./tool/go build ./cmd/tsconnect/wasm ./cmd/tailscale/cli
  322. env:
  323. GOOS: js
  324. GOARCH: wasm
  325. - name: build tsconnect server
  326. # Note, no GOOS/GOARCH in env on this build step, we're running a build
  327. # tool that handles the build itself.
  328. run: |
  329. ./tool/go run ./cmd/tsconnect --fast-compression build
  330. ./tool/go run ./cmd/tsconnect --fast-compression build-pkg
  331. tailscale_go: # Subset of tests that depend on our custom Go toolchain.
  332. runs-on: ubuntu-22.04
  333. steps:
  334. - name: checkout
  335. uses: actions/checkout@v4
  336. - name: test tailscale_go
  337. run: ./tool/go test -tags=tailscale_go,ts_enable_sockstats ./net/sockstats/...
  338. fuzz:
  339. # This target periodically breaks (see TS_FUZZ_CURRENTLY_BROKEN at the top
  340. # of the file), so it's more complex than usual: the 'build fuzzers' step
  341. # might fail, and depending on the value of 'TS_FUZZ_CURRENTLY_BROKEN', that
  342. # might or might not be fine. The steps after the build figure out whether
  343. # the success/failure is expected, and appropriately pass/fail the job
  344. # overall accordingly.
  345. #
  346. # Practically, this means that all steps after 'build fuzzers' must have an
  347. # explicit 'if' condition, because the default condition for steps is
  348. # 'success()', meaning "only run this if no previous steps failed".
  349. if: github.event_name == 'pull_request'
  350. runs-on: ubuntu-22.04
  351. steps:
  352. - name: build fuzzers
  353. id: build
  354. uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
  355. # continue-on-error makes steps.build.conclusion be 'success' even if
  356. # steps.build.outcome is 'failure'. This means this step does not
  357. # contribute to the job's overall pass/fail evaluation.
  358. continue-on-error: true
  359. with:
  360. oss-fuzz-project-name: 'tailscale'
  361. dry-run: false
  362. language: go
  363. - name: report unexpectedly broken fuzz build
  364. if: steps.build.outcome == 'failure' && env.TS_FUZZ_CURRENTLY_BROKEN != 'true'
  365. run: |
  366. echo "fuzzer build failed, see above for why"
  367. echo "if the failure is due to OSS-Fuzz not being on the latest Go yet,"
  368. echo "set TS_FUZZ_CURRENTLY_BROKEN=true in .github/workflows/test.yml"
  369. echo "to temporarily disable fuzzing until OSS-Fuzz works again."
  370. exit 1
  371. - name: report unexpectedly working fuzz build
  372. if: steps.build.outcome == 'success' && env.TS_FUZZ_CURRENTLY_BROKEN == 'true'
  373. run: |
  374. echo "fuzzer build succeeded, but we expect it to be broken"
  375. echo "please set TS_FUZZ_CURRENTLY_BROKEN=false in .github/workflows/test.yml"
  376. echo "to reenable fuzz testing"
  377. exit 1
  378. - name: run fuzzers
  379. id: run
  380. # Run the fuzzers whenever they're able to build, even if we're going to
  381. # report a failure because TS_FUZZ_CURRENTLY_BROKEN is set to the wrong
  382. # value.
  383. if: steps.build.outcome == 'success'
  384. uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
  385. with:
  386. oss-fuzz-project-name: 'tailscale'
  387. fuzz-seconds: 300
  388. dry-run: false
  389. language: go
  390. - name: upload crash
  391. uses: actions/upload-artifact@v3
  392. if: steps.run.outcome != 'success' && steps.build.outcome == 'success'
  393. with:
  394. name: artifacts
  395. path: ./out/artifacts
  396. depaware:
  397. runs-on: ubuntu-22.04
  398. steps:
  399. - name: checkout
  400. uses: actions/checkout@v4
  401. - name: check depaware
  402. run: |
  403. export PATH=$(./tool/go env GOROOT)/bin:$PATH
  404. find . -name 'depaware.txt' | xargs -n1 dirname | xargs ./tool/go run github.com/tailscale/depaware --check
  405. go_generate:
  406. runs-on: ubuntu-22.04
  407. steps:
  408. - name: checkout
  409. uses: actions/checkout@v4
  410. - name: check that 'go generate' is clean
  411. run: |
  412. pkgs=$(./tool/go list ./... | grep -Ev 'dnsfallback|k8s-operator')
  413. ./tool/go generate $pkgs
  414. echo
  415. echo
  416. git diff --name-only --exit-code || (echo "The files above need updating. Please run 'go generate'."; exit 1)
  417. go_mod_tidy:
  418. runs-on: ubuntu-22.04
  419. steps:
  420. - name: checkout
  421. uses: actions/checkout@v4
  422. - name: check that 'go mod tidy' is clean
  423. run: |
  424. ./tool/go mod tidy
  425. echo
  426. echo
  427. git diff --name-only --exit-code || (echo "Please run 'go mod tidy'."; exit 1)
  428. licenses:
  429. runs-on: ubuntu-22.04
  430. steps:
  431. - name: checkout
  432. uses: actions/checkout@v4
  433. - name: check licenses
  434. run: ./scripts/check_license_headers.sh .
  435. staticcheck:
  436. runs-on: ubuntu-22.04
  437. strategy:
  438. fail-fast: false # don't abort the entire matrix if one element fails
  439. matrix:
  440. goos: ["linux", "windows", "darwin"]
  441. goarch: ["amd64"]
  442. include:
  443. - goos: "windows"
  444. goarch: "386"
  445. steps:
  446. - name: checkout
  447. uses: actions/checkout@v4
  448. - name: install staticcheck
  449. run: GOBIN=~/.local/bin ./tool/go install honnef.co/go/tools/cmd/staticcheck
  450. - name: run staticcheck
  451. run: |
  452. export GOROOT=$(./tool/go env GOROOT)
  453. export PATH=$GOROOT/bin:$PATH
  454. staticcheck -- $(./tool/go list ./... | grep -v tempfork)
  455. env:
  456. GOOS: ${{ matrix.goos }}
  457. GOARCH: ${{ matrix.goarch }}
  458. notify_slack:
  459. if: always()
  460. # Any of these jobs failing causes a slack notification.
  461. needs:
  462. - android
  463. - test
  464. - windows
  465. - vm
  466. - cross
  467. - ios
  468. - wasm
  469. - tailscale_go
  470. - fuzz
  471. - depaware
  472. - go_generate
  473. - go_mod_tidy
  474. - licenses
  475. - staticcheck
  476. runs-on: ubuntu-22.04
  477. steps:
  478. - name: notify
  479. # Only notify slack for merged commits, not PR failures.
  480. #
  481. # It may be tempting to move this condition into the job's 'if' block, but
  482. # don't: Github only collapses the test list into "everything is OK" if
  483. # all jobs succeeded. A skipped job results in the list staying expanded.
  484. # By having the job always run, but skipping its only step as needed, we
  485. # let the CI output collapse nicely in PRs.
  486. if: failure() && github.event_name == 'push'
  487. uses: ruby/[email protected]
  488. with:
  489. payload: |
  490. {
  491. "attachments": [{
  492. "title": "Failure: ${{ github.workflow }}",
  493. "title_link": "https://github.com/${{ github.repository }}/commit/${{ github.sha }}/checks",
  494. "text": "${{ github.repository }}@${{ github.ref_name }}: <https://github.com/${{ github.repository }}/commit/${{ github.sha }}|${{ github.sha }}>",
  495. "fields": [{ "value": ${{ toJson(github.event.head_commit.message) }}, "short": false }],
  496. "footer": "${{ github.event.head_commit.committer.name }} at ${{ github.event.head_commit.timestamp }}",
  497. "color": "danger"
  498. }]
  499. }
  500. env:
  501. SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
  502. check_mergeability:
  503. if: always()
  504. runs-on: ubuntu-22.04
  505. needs:
  506. - android
  507. - test
  508. - windows
  509. - vm
  510. - cross
  511. - ios
  512. - wasm
  513. - tailscale_go
  514. - fuzz
  515. - depaware
  516. - go_generate
  517. - go_mod_tidy
  518. - licenses
  519. - staticcheck
  520. steps:
  521. - name: Decide if change is okay to merge
  522. if: github.event_name != 'push'
  523. uses: re-actors/alls-green@release/v1
  524. with:
  525. jobs: ${{ toJSON(needs) }}