Skip to content

Unified Release Workflow #137

Unified Release Workflow

Unified Release Workflow #137

Workflow file for this run

name: Unified Release Workflow
on:
push:
branches:
- main
paths:
- ".github/workflows/release.yml"
- "generate-tar.sh"
- "etc/*"
- "usr/bin/tailscale-enabler"
pull_request:
branches:
- main
schedule:
- cron: "0 0 * * *" # Every day at 00:00 UTC
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
include:
# Linux
- goos: linux
goarch: amd64
- goos: linux
goarch: arm64
- goos: linux
goarch: "386"
- goos: linux
goarch: loong64
- goos: linux
goarch: arm
goarm: "5"
- goos: linux
goarch: arm
goarm: "7"
- goos: linux
goarch: mips
- goos: linux
goarch: mipsle
- goos: linux
goarch: mips64
- goos: linux
goarch: mips64le
# macOS
- goos: darwin
goarch: amd64
- goos: darwin
goarch: arm64
# Windows
- goos: windows
goarch: amd64
- goos: windows
goarch: arm64
# BSDs
- goos: freebsd
goarch: amd64
- goos: openbsd
goarch: amd64
permissions:
contents: write
pull-requests: write
repository-projects: write
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Get latest non-pre Tailscale tag
id: get_tag
run: |
git clone https://github.com/tailscale/tailscale.git
cd tailscale
LATEST_TAG=$(git tag -l --sort=-v:refname | grep -v -E '(pre|rc)' | head -n 1)
echo "::set-output name=latest_tag::$LATEST_TAG"
- name: Check if tag already built
id: check_tag
run: |
if git rev-parse ${{ steps.get_tag.outputs.latest_tag }} >/dev/null 2>&1; then
echo "::set-output name=tag_exists::true"
else
echo "::set-output name=tag_exists::false"
fi
- name: Set Version Variables
run: |
cd tailscale
VERSION_SHORT=$(git describe --tags --abbrev=0)
VERSION_GIT_HASH=$(git rev-parse --short HEAD)
VERSION_LONG="${VERSION_SHORT}-t${VERSION_GIT_HASH}"
echo "VERSION_SHORT=${VERSION_SHORT}"
echo "VERSION_GIT_HASH=${VERSION_GIT_HASH}"
echo "VERSION_LONG=${VERSION_LONG}"
- name: Set up Go environment variables
run: |
echo "GOOS=${{ matrix.goos }}" >> $GITHUB_ENV
echo "GOARCH=${{ matrix.goarch }}" >> $GITHUB_ENV
if [ -n "${{ matrix.goarm }}" ]; then
echo "GOARM=${{ matrix.goarm }}" >> $GITHUB_ENV
fi
- name: Set up Go
if: steps.check_tag.outputs.tag_exists == 'false'
uses: actions/setup-go@v2
with:
go-version: "^1.17"
- name: Build smaller Tailscale binary
if: steps.check_tag.outputs.tag_exists == 'false' && matrix.goos != 'windows'
run: |
set -eux # Add debugging information
cd tailscale
git checkout ${{ steps.get_tag.outputs.latest_tag }}
eval $(CGO_ENABLED=0 GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} go run ./cmd/mkversion)
go build -o tailscale.combined -ldflags "-s -w" -tags "ts_include_cli,ts_omit_aws,ts_omit_bird,ts_omit_tap,ts_omit_kube,ts_omit_completion" ./cmd/tailscaled
cp tailscale.combined ../tailscale.combined
cp tailscale.combined ../tailscale-small-${{ matrix.goos }}-${{ matrix.goarch }}-${{ steps.get_tag.outputs.latest_tag }}
- name: Build smaller Tailscale binary for Windows
if: steps.check_tag.outputs.tag_exists == 'false' && matrix.goos == 'windows'
run: |
set -eux # Add debugging information
cd tailscale
git checkout ${{ steps.get_tag.outputs.latest_tag }}
eval $(CGO_ENABLED=0 GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} go run ./cmd/mkversion)
go build -o tailscale.combined.exe -ldflags "-s -w" -tags "ts_include_cli,ts_omit_aws,ts_omit_bird,ts_omit_tap,ts_omit_kube,ts_omit_completion" ./cmd/tailscaled
cp tailscale.combined.exe ../tailscale-small-${{ matrix.goos }}-${{ matrix.goarch }}-${{ steps.get_tag.outputs.latest_tag }}.exe
- name: Install UPX
if: steps.check_tag.outputs.tag_exists == 'false' && matrix.goos != 'windows'
run: sudo apt-get update && sudo apt-get install -y upx
- name: Compress with UPX
if: steps.check_tag.outputs.tag_exists == 'false' && matrix.goos != 'windows'
run: |
upx --lzma --best ./tailscale.combined
mv tailscale.combined tailscale-small-upx-${{ matrix.goos }}-${{ matrix.goarch }}-${{ steps.get_tag.outputs.latest_tag }}
continue-on-error: true
- name: Check UPX Compression
if: steps.check_tag.outputs.tag_exists == 'false'
id: check_upx
run: |
if [ -f tailscale-small-upx-${{ matrix.goos }}-${{ matrix.goarch }}-${{ steps.get_tag.outputs.latest_tag }} ]; then
echo "UPX_EXISTS=true" >> $GITHUB_ENV
else
echo "UPX_EXISTS=false" >> $GITHUB_ENV
fi
- name: Upload Artifacts
if: steps.check_tag.outputs.tag_exists == 'false' && matrix.goos != 'windows'
uses: actions/upload-artifact@v3
with:
name: tailscale-binaries
path: |
tailscale-small-${{ matrix.goos }}-${{ matrix.goarch }}-${{ steps.get_tag.outputs.latest_tag }}
${{ env.UPX_EXISTS == 'true' && format('tailscale-small-upx-{0}-{1}-{2}', matrix.goos, matrix.goarch, steps.get_tag.outputs.latest_tag) || '' }}
- name: Upload Artifacts for Windows
if: steps.check_tag.outputs.tag_exists == 'false' && matrix.goos == 'windows'
uses: actions/upload-artifact@v3
with:
name: tailscale-binaries
path: |
tailscale-small-${{ matrix.goos }}-${{ matrix.goarch }}-${{ steps.get_tag.outputs.latest_tag }}.exe
release:
needs: build
runs-on: ubuntu-latest
steps:
- name: Checkout code
id: checkout_code
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Get latest non-pre Tailscale tag
id: get_tag
run: |
git clone https://github.com/tailscale/tailscale.git
cd tailscale
LATEST_TAG=$(git tag -l --sort=-v:refname | grep -v -E '(pre|rc)' | head -n 1)
echo "::set-output name=latest_tag::$LATEST_TAG"
- name: Get data
id: data
run: |
echo "##[set-output name=version;]${{ steps.get_tag.outputs.latest_tag }}-$(git rev-parse --short HEAD)-autoupdate"
echo "##[set-output name=commit;]$(git rev-parse HEAD)"
# also get repo link
echo "##[set-output name=repolink;]$(git remote get-url origin | sed 's/\.git//g')"
- name: Generate tar archive
run: ./generate-tar.sh ${{ steps.data.outputs.version }}
- name: Check if tag already built
id: check_tag
run: |
if git rev-parse ${{ steps.get_tag.outputs.latest_tag }} >/dev/null 2>&1; then
echo "::set-output name=tag_exists::true"
else
echo "::set-output name=tag_exists::false"
fi
- name: Download Artifacts
if: steps.check_tag.outputs.tag_exists == 'false'
uses: actions/download-artifact@v3
with:
name: tailscale-binaries
path: ./binaries
continue-on-error: true
- name: Create Release New
if: steps.check_tag.outputs.tag_exists == 'false'
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.get_tag.outputs.latest_tag }}
release_name: Tailscale ${{ steps.get_tag.outputs.latest_tag }}
body: |
This release contains two versions of the Tailscale binary for each platform:
- Non-compressed version
- UPX-compressed version
The UPX-compressed version is smaller but may trigger false positives in some security software.
And, build for all the following platforms:
- Linux (amd64, arm64, 386, loong64, arm5, arm7)
- macOS (amd64)
- FreeBSD (amd64)
- OpenBSD (amd64)
- Windows (amd64, arm64)
- mips (mips, mipsle, mips64, mips64le)
Some of the binaries are compressed with UPX, while the
- Windows
- mips64
- mips64le
- loong64
- freebsd
- openbsd
ones are not compressed as UPX doesn't support them.
The binaries are built from the latest non-pre Tailscale tag using the official build script with the extra-small flag, Refer to the [Tailscale GitHub repository](https://github.com/tailscale/tailscale)
They're much smaller as I've used the techniques from the [Tailscale blog post](https://tailscale.com/kb/1207/small-tailscale) to make them smaller.
It also includes the tar archive for easy install.
To install: `tar x -zvC / -f openwrt-tailscale-enabler-${{ steps.data.outputs.version }}.tgz`.
Then run `tailscale-enabler`.
If you want, you can also set cron jobs to update the Tailscale binary automatically.
In a pasteable format, the commands are:
```
# Download the enabler script
wget ${{ steps.data.outputs.repolink }}/releases/download/${{ steps.get_tag.outputs.latest_tag }}/openwrt-tailscale-enabler-${{ steps.data.outputs.version }}.tgz
tar x -zvC / -f openwrt-tailscale-enabler-${{ steps.data.outputs.version }}.tgz
rm openwrt-tailscale-enabler-${{ steps.data.outputs.version }}.tgz
# Download the latest Tailscale binary
/usr/bin/tailscale-enabler
# Install the required packages
opkg update
opkg install libustream-openssl ca-bundle kmod-tun
# Start the Tailscale service
/etc/init.d/tailscale start
# Start the Tailscale client. You may need to authenticate it using the provided link.
tailscale up --accept-dns=false
# Enable Tailscale to start on boot
/etc/init.d/tailscale enable
# to Auto Update Tailscale binary every Sunday at 00:00
{ crontab -l; echo "0 0 * * 0 /usr/bin/tailscale-enabler"; } | crontab -
```
Further details can be found in the repo readme.
draft: false
prerelease: false
# check if the tag already exists
- name: Check if tag already exists
id: check_tag_existing
run: |
if git rev-parse ${{ steps.data.outputs.version }} >/dev/null 2>&1; then
echo "::set-output name=tag_exists::true"
else
echo "::set-output name=tag_exists::false"
fi
- name: Create Release Existing
if: steps.check_tag.outputs.tag_exists == 'true'
id: create_release_existing
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.data.outputs.version }}
release_name: Tailscale ${{ steps.data.outputs.version }}
body: |
Auto Update
draft: false
prerelease: true
- name: Upload Release Assets
uses: actions/github-script@v6
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
const fs = require('fs').promises;
const path = require('path');
const uploadAsset = async (releaseId, file) => {
console.log(`Uploading ${file}...`);
await github.rest.repos.uploadReleaseAsset({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: releaseId,
name: path.basename(file),
data: await fs.readFile(file)
});
};
const releaseId = ${{ steps.create_release.outputs.id || steps.create_release_existing.outputs.id }};
const enablerPath = './openwrt-tailscale-enabler-${{ steps.data.outputs.version }}.tgz';
await uploadAsset(releaseId, enablerPath);
- name: Upload Release Assets
if: steps.check_tag.outputs.tag_exists == 'false'
uses: actions/github-script@v6
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
const fs = require('fs').promises;
const path = require('path');
const uploadAsset = async (releaseId, file) => {
console.log(`Uploading ${file}...`);
await github.rest.repos.uploadReleaseAsset({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: releaseId,
name: path.basename(file),
data: await fs.readFile(file)
});
};
const releaseId = ${{ steps.create_release.outputs.id || steps.create_release_existing.outputs.id }};
let files = await fs.readdir('./binaries');
for (const file of files) {
await uploadAsset(releaseId, path.join('./binaries', file));
}