test.yml 15 KB

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