-
Notifications
You must be signed in to change notification settings - Fork 1
177 lines (160 loc) · 6.84 KB
/
release.yml
File metadata and controls
177 lines (160 loc) · 6.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
name: release
# Conventional-commit release pipeline. On push to main (or manual dispatch),
# semantic-release computes the next version from commits, then via the plugins in
# .releaserc.json: builds cross-platform binaries (goreleaser), publishes the npm
# launcher + per-platform packages, pushes the Homebrew cask, and creates the
# GitHub release with the binary archives attached.
on:
workflow_dispatch:
inputs:
dry-run:
description: "Run semantic-release in --dry-run mode (no publish)"
required: false
type: boolean
default: false
push:
branches: [master]
# No path filter: semantic-release decides from the commit history whether a
# release is warranted (it no-ops for docs/chore/ci-only changes).
# Never cancel an in-flight release — interrupting between npm publish and the
# git tag / GitHub release leaves the registry inconsistent. Queue instead.
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: read
env:
# GitHub forbids `secrets.*` directly in some `if:` expressions, so expose
# boolean flags once here and reuse them in the notify steps below.
HAS_SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL != '' }}
HAS_DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL != '' }}
jobs:
release:
name: Publish
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: write # semantic-release pushes tags + GitHub release
issues: write # release comments on released issues
pull-requests: write # release comments on released PRs
steps:
- name: Check release secrets
id: gate
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
if [ -z "$NPM_TOKEN" ]; then
echo "NPM_TOKEN is not set — skipping release. Add the secret to enable publishing."
echo "enabled=false" >> "$GITHUB_OUTPUT"
else
echo "enabled=true" >> "$GITHUB_OUTPUT"
fi
- uses: actions/checkout@v6
if: steps.gate.outputs.enabled == 'true'
with:
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-go@v6
if: steps.gate.outputs.enabled == 'true'
with:
go-version: "stable"
- uses: actions/setup-node@v6
if: steps.gate.outputs.enabled == 'true'
with:
node-version: "22"
registry-url: "https://registry.npmjs.org"
- uses: oven-sh/setup-bun@v2
if: steps.gate.outputs.enabled == 'true'
with:
bun-version: "1.3"
- name: Install goreleaser
if: steps.gate.outputs.enabled == 'true'
uses: goreleaser/goreleaser-action@v7
with:
version: "~> v2"
install-only: true
- name: Install dependencies
if: steps.gate.outputs.enabled == 'true'
run: bun install
- name: Verify
if: steps.gate.outputs.enabled == 'true'
run: |
test -z "$(gofmt -l .)" || { echo "gofmt needed:"; gofmt -l .; exit 1; }
go vet ./...
go test -race -count=1 ./...
- name: Semantic release
if: steps.gate.outputs.enabled == 'true'
run: |
if [ "${{ inputs.dry-run }}" = "true" ]; then
bunx semantic-release --dry-run
else
bunx semantic-release
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.GH_PAT }}
- name: Release summary
if: always() && steps.gate.outputs.enabled == 'true'
env:
REPO: ${{ github.repository }}
DRY: ${{ inputs.dry-run }}
run: |
# semantic-release stamps package.json to the released version during the
# run; 0.0.0 means nothing was released.
VER=$(node -p "require('./package.json').version" 2>/dev/null || echo "")
{
echo "## 📦 tailscale-proxy release"
echo
if [ "$DRY" = "true" ]; then
echo "🟡 **Dry run** — nothing was published."
elif [ -n "$VER" ] && [ "$VER" != "0.0.0" ]; then
echo "✅ **Published \`v$VER\`**"
echo
echo "| | |"
echo "|---|---|"
echo "| npm | \`npx tailscale-proxy@$VER\` · [package](https://www.npmjs.com/package/tailscale-proxy/v/$VER) |"
echo "| GitHub release | [v$VER](https://github.com/$REPO/releases/tag/v$VER) |"
echo "| Homebrew | \`brew install meabed/tap/tsp\` |"
echo
echo "Platforms: darwin/linux/windows × amd64/arm64."
else
echo "ℹ️ **No release** — no \`feat\`/\`fix\`/\`perf\` commits since the last tag."
fi
} >> "$GITHUB_STEP_SUMMARY"
- name: Notify Slack
# !cancelled() → notify on success and failure, skip superseded builds; only
# when a real release attempt ran (gate enabled). Each step self-skips
# without its webhook secret.
if: ${{ !cancelled() && steps.gate.outputs.enabled == 'true' && env.HAS_SLACK_WEBHOOK_URL == 'true' }}
uses: slackapi/slack-github-action@v3.0.3
with:
webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
webhook-type: incoming-webhook
# Status-aware. Only trusted GitHub context fields are interpolated (injection-safe).
payload: |
text: "${{ job.status == 'success' && ':white_check_mark:' || ':x:' }} ${{ github.repository }} release ${{ job.status }} on ${{ github.ref_name }}"
blocks:
- type: "section"
text:
type: "mrkdwn"
text: "${{ job.status == 'success' && ':white_check_mark:' || ':x:' }} *${{ github.repository }}* release *${{ job.status }}* on `${{ github.ref_name }}`\n*Workflow:* ${{ github.workflow }} • *By:* ${{ github.actor }} • *Commit:* `${{ github.sha }}`"
- type: "actions"
elements:
- type: "button"
text:
type: "plain_text"
text: "View run"
url: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
- name: Notify Discord
if: ${{ !cancelled() && steps.gate.outputs.enabled == 'true' && env.HAS_DISCORD_WEBHOOK_URL == 'true' }}
uses: sarisia/actions-status-discord@v1
with:
webhook: ${{ secrets.DISCORD_WEBHOOK_URL }}
status: ${{ job.status }}
title: ${{ github.repository }}
# Ping @here only when it needs attention (failure); quiet on success.
content: "${{ job.status != 'success' && '@here' || '' }}"
username: GitHub Actions
nofail: true