-
Notifications
You must be signed in to change notification settings - Fork 0
270 lines (240 loc) · 12.5 KB
/
Copy pathrelease.yml
File metadata and controls
270 lines (240 loc) · 12.5 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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
name: release
on:
# Dispatched as well as pushed to: auto-release.yml creates the tag with GITHUB_TOKEN when a
# release note is merged, and a tag pushed with that token deliberately does not trigger
# `on: push: tags`. workflow_dispatch is documented as an exception that always creates a run.
# Dispatched at the tag's ref, so github.ref_name below is the tag either way.
workflow_dispatch:
push:
tags: ['v*']
concurrency:
group: release-${{ github.ref_name }}
cancel-in-progress: false
permissions:
contents: read
jobs:
# The release path packs and publishes without re-running validate/sample/e2e, on the grounds
# that the tagged commit already went through them on its pull request. That reasoning only
# holds if the commit is genuinely on the default branch — a tag cut from an unmerged branch,
# or from a commit force-pushed away since, would ship having been verified by nothing. Two
# cheap ubuntu minutes to make the assumption explicit rather than implicit.
guard:
name: verify the tag is on the default branch
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Refuse a tag that never went through a pull request
env:
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
run: |
set -euo pipefail
# A tag push checks out the tag, and the default branch's ref is not necessarily among
# the refs fetched with it, so ask for it by name before testing ancestry.
git fetch --no-tags --quiet origin \
"+refs/heads/${DEFAULT_BRANCH}:refs/remotes/origin/${DEFAULT_BRANCH}"
if ! git merge-base --is-ancestor "${GITHUB_SHA}" "origin/${DEFAULT_BRANCH}"; then
echo "::error::${GITHUB_REF_NAME} points at ${GITHUB_SHA}, which is not an ancestor of ${DEFAULT_BRANCH}. Releases skip the test suites because the tagged commit was verified on its pull request; this commit was not. Merge it first, then re-tag."
exit 1
fi
echo "${GITHUB_REF_NAME} -> ${GITHUB_SHA} is on ${DEFAULT_BRANCH}" >> "$GITHUB_STEP_SUMMARY"
version:
name: resolve release version
runs-on: ubuntu-latest
outputs:
version: ${{ steps.resolve.outputs.version }}
native-version: ${{ steps.resolve.outputs.native-version }}
prerelease: ${{ steps.resolve.outputs.prerelease }}
steps:
- uses: actions/checkout@v4
- id: resolve
run: |
version="${GITHUB_REF_NAME#v}"
if ! printf '%s' "${version}" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?(-[0-9A-Za-z.-]+)?$'; then
echo "::error::tag '${GITHUB_REF_NAME}' does not look like a version (expected e.g. v2.17.0.1)"
exit 1
fi
# The tag drives which native SDK is bound, so a different line is released simply by
# tagging it - v2.18.0.1 builds against dd-sdk-ios 2.18.0. Any prerelease suffix is
# dropped, and the fourth component is this repository's binding revision, not part of
# the Datadog version: v2.17.0.6 still binds dd-sdk-ios 2.17.0.
native=$(printf '%s' "${version%%-*}" | cut -d. -f1-3)
echo "native-version=${native}" >> "$GITHUB_OUTPUT"
case "${version}" in
*-*) prerelease=true ;;
*) prerelease=false ;;
esac
echo "version=${version}" >> "$GITHUB_OUTPUT"
echo "prerelease=${prerelease}" >> "$GITHUB_OUTPUT"
echo "Releasing ${version} (prerelease=${prerelease})" >> "$GITHUB_STEP_SUMMARY"
# The tag names the dd-sdk-ios line to bind; Directory.Build.props names the line this
# branch is developed and tested against. When they disagree, the release would publish
# packages binding a native SDK the committed ApiDefinitions.cs were never ported to - or
# simply a mistyped tag - so it stops here, before anything builds. Fix the tag (delete and
# re-push) or bump the props. Releasing a different line on purpose - v2.30.2.3 from a main
# that has moved on to 3.x - is still possible: set the repository variable
# RELEASE_ALLOW_VERSION_MISMATCH to 'true' for that release, and unset it afterwards.
- name: Check the tag against Directory.Build.props
if: vars.RELEASE_ALLOW_VERSION_MISMATCH != 'true'
run: |
version="${{ steps.resolve.outputs.version }}"
native="${{ steps.resolve.outputs.native-version }}"
props_native=$(sed -n 's:.*<DatadogNativeVersion>\(.*\)</DatadogNativeVersion>.*:\1:p' Directory.Build.props | head -1)
props_revision=$(sed -n 's:.*<DatadogBindingRevision>\(.*\)</DatadogBindingRevision>.*:\1:p' Directory.Build.props | head -1)
if [ "${native}" != "${props_native}" ]; then
echo "::error::tag ${GITHUB_REF_NAME} binds dd-sdk-ios ${native}, but Directory.Build.props has DatadogNativeVersion ${props_native}. Fix the tag or bump the props; for a deliberate different-line release, set the RELEASE_ALLOW_VERSION_MISMATCH repository variable to 'true'."
exit 1
fi
# The fourth component is this repository's binding revision. A tag may omit it, and a
# prerelease carries a suffix; both are stripped before comparing.
revision=$(printf '%s' "${version%%-*}" | cut -s -d. -f4)
if [ -n "${revision}" ] && [ "${revision}" != "${props_revision}" ]; then
echo "::error::tag ${GITHUB_REF_NAME} is binding revision ${revision}, but Directory.Build.props has DatadogBindingRevision ${props_revision}. Fix the tag or bump the props; for a deliberate mismatch, set the RELEASE_ALLOW_VERSION_MISMATCH repository variable to 'true'."
exit 1
fi
echo "Tag agrees with Directory.Build.props (${props_native}, revision ${props_revision})" >> "$GITHUB_STEP_SUMMARY"
build:
name: build
needs: [guard, version]
uses: ./.github/workflows/build.yml
with:
version: ${{ needs.version.outputs.version }}
native-version: ${{ needs.version.outputs.native-version }}
# Verification already happened on this commit's pull request, and the guard job above
# proved the tag points at that commit. A release packs and publishes, nothing more.
verify: false
publish:
name: publish to nuget.org and create release
needs: [version, build]
runs-on: ubuntu-latest
# Must match the Environment on the nuget.org trusted publishing policy for this workflow.
environment: nuget.org
permissions:
# contents: write creates the GitHub release; id-token: write lets the job request the OIDC
# token that nuget.org exchanges for a short-lived API key. Without the latter the token
# request fails silently and the login step gets no key.
contents: write
id-token: write
env:
VERSION: ${{ needs.version.outputs.version }}
steps:
- uses: actions/checkout@v4
with:
# Needed to find the previous tag when generating the changelog.
fetch-depth: 0
- name: Set up .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.0.x
- name: Download packages
uses: actions/download-artifact@v4
with:
name: nuget-packages
path: artifacts
# nuget.org is published to first: a GitHub release that links to packages which failed to
# upload would be worse than a release created a moment late.
# Deliberately immediately before the push: the issued key is valid for one hour, and each
# OIDC token can be exchanged exactly once.
- name: Authenticate to nuget.org
id: nuget-login
uses: NuGet/login@v1
with:
user: ${{ secrets.NUGET_USER }}
- name: Push to nuget.org
run: |
dotnet nuget push "artifacts/*.nupkg" \
--source https://api.nuget.org/v3/index.json \
--api-key "${{ steps.nuget-login.outputs.NUGET_API_KEY }}" \
--skip-duplicate
- name: Compose release notes
run: |
# Resolved from the tag, not Directory.Build.props, so a release of a different line
# reports the version it actually bound rather than whatever the branch defaults to.
native="${{ needs.version.outputs.native-version }}"
previous=$(git describe --tags --abbrev=0 "${GITHUB_REF_NAME}^" 2>/dev/null || true)
# A curated docs/release-notes/<version>.md replaces the generated commit list when one
# exists. The second lookup drops the prerelease suffix, so v2.17.0.1-beta.2 reuses the
# notes written for 2.17.0.1 rather than silently falling back to raw commit subjects.
notes="docs/release-notes/${VERSION}.md"
[ -f "${notes}" ] || notes="docs/release-notes/${VERSION%%-*}.md"
{
if [ -f "${notes}" ]; then
echo "==> using curated notes from ${notes}" >&2
cat "${notes}"
else
echo "## What's changed"
echo
if [ -n "${previous}" ]; then
git log --no-merges --pretty='- %s (%h)' "${previous}..${GITHUB_REF_NAME}"
else
# No previous tag: listing every commit would reach back to the repository's first
# commit, which is noise rather than a changelog.
echo "Initial release."
fi
fi
echo
if [ -n "${previous}" ]; then
echo "**Full changelog**: ${{ github.server_url }}/${{ github.repository }}/compare/${previous}...${GITHUB_REF_NAME}"
echo
fi
echo "## Packages"
echo
echo "Bound against [dd-sdk-ios \`${native}\`](https://github.com/DataDog/dd-sdk-ios/releases/tag/${native}), targeting \`net8.0-ios18.0\`, \`net9.0-ios18.0\` and \`net10.0-ios26.0\`."
echo
echo "> The first three components of \`${VERSION}\` are the dd-sdk-ios version; the fourth is this"
echo "> repository's binding revision, which advances when the bindings or packaging change while"
echo "> the native binaries stay put."
echo
echo "| Package | Wraps | NuGet |"
echo "| --- | --- | --- |"
# Package id, then the native framework it ships. Kept in the same order as the README.
while IFS=: read -r package framework; do
[ -n "${package}" ] || continue
id="DatadogNet.${package}.iOS"
echo "| \`${id}\` | \`${framework}\` | [${VERSION}](https://www.nuget.org/packages/${id}/${VERSION}) |"
done <<'PACKAGES'
Core:DatadogCore
RUM:DatadogRUM
Logs:DatadogLogs
Trace:DatadogTrace
SessionReplay:DatadogSessionReplay
WebViewTracking:DatadogWebViewTracking
CrashReporting:DatadogCrashReporting
Flags:DatadogFlags
Profiling:DatadogProfiling
Internal:DatadogInternal
OpenTelemetryApi:OpenTelemetryApi
Objc:(compatibility meta-package)
PACKAGES
echo
echo "\`\`\`"
echo "dotnet add package DatadogNet.Core.iOS --version ${VERSION}"
echo "dotnet add package DatadogNet.RUM.iOS --version ${VERSION}"
echo "\`\`\`"
echo
echo "## Licensing"
echo
echo "The binding code is MIT; the bundled native binaries are Apache-2.0, as built and"
echo "published by Datadog. Each package ships both texts under \`licenses/\`."
} > release-notes.md
cat release-notes.md >> "$GITHUB_STEP_SUMMARY"
- name: Create GitHub release
env:
GH_TOKEN: ${{ github.token }}
run: |
# The packages themselves are not attached: nuget.org is the distribution channel, and
# the notes link to every published package instead.
gh release create "${GITHUB_REF_NAME}" \
--title "${VERSION}" \
--notes-file release-notes.md \
${{ needs.version.outputs.prerelease == 'true' && '--prerelease' || '' }}
# The parent repositories pin these packages by exact version; tell them what just shipped
# rather than leaving it to their daily drift check. Who cares, and about which packages,
# lives in build/parents.tsv. Skips itself quietly while PARENT_ISSUE_PAT does not exist.
notify-parents:
needs: publish
uses: ./.github/workflows/notify-parents.yml
with:
tag: ${{ github.ref_name }}
secrets: inherit