Skip to content

Commit cf3656a

Browse files
authored
feat(ci): fix release for csharp and dart (#3582)
## Why? ## What does this PR do? - auto release for c# - fix release for dart - auto release dart ## Related issues ## AI Contribution Checklist - [ ] Substantial AI assistance was used in this PR: `yes` / `no` - [ ] If `yes`, I included a completed [AI Contribution Checklist](https://github.com/apache/fory/blob/main/AI_POLICY.md#9-contributor-checklist-for-ai-assisted-prs) in this PR description and the required `AI Usage Disclosure`. - [ ] If `yes`, my PR description includes the required `ai_review` summary and screenshot evidence of the final clean AI review results from both fresh reviewers on the current PR diff or current HEAD after the latest code changes. ## Does this PR introduce any user-facing change? - [ ] Does this PR introduce any public API change? - [ ] Does this PR introduce any binary protocol compatibility change? ## Benchmark
1 parent 9c82aaa commit cf3656a

6 files changed

Lines changed: 611 additions & 0 deletions

File tree

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
name: Publish C#
19+
run-name: "C# Release: ${{ github.ref_name }}"
20+
21+
on:
22+
push:
23+
tags: ['v*']
24+
25+
permissions:
26+
contents: read
27+
id-token: write
28+
29+
concurrency:
30+
group: release-csharp-${{ github.ref }}
31+
cancel-in-progress: false
32+
33+
jobs:
34+
publish-csharp:
35+
runs-on: ubuntu-latest
36+
if: github.repository == 'apache/fory'
37+
env:
38+
NUGET_SOURCE: https://api.nuget.org/v3/index.json
39+
steps:
40+
- uses: actions/checkout@v5
41+
42+
- uses: actions/setup-python@v6
43+
with:
44+
python-version: '3.11'
45+
cache: 'pip'
46+
47+
- name: Bump C# version
48+
shell: bash
49+
run: |
50+
set -euo pipefail
51+
VERSION="${{ github.ref_name }}"
52+
VERSION="${VERSION#v}"
53+
python ci/release.py bump_version -l csharp -version "$VERSION"
54+
55+
- name: Set up .NET 8
56+
uses: actions/setup-dotnet@v5
57+
with:
58+
dotnet-version: "8.0.x"
59+
cache: true
60+
cache-dependency-path: |
61+
csharp/**/*.csproj
62+
csharp/Fory.sln
63+
64+
- name: Restore C# dependencies
65+
shell: bash
66+
working-directory: csharp
67+
run: |
68+
set -euo pipefail
69+
dotnet restore Fory.sln
70+
71+
- name: Build C# solution
72+
shell: bash
73+
working-directory: csharp
74+
run: |
75+
set -euo pipefail
76+
dotnet build Fory.sln -c Release --no-restore
77+
78+
- name: Run C# tests
79+
shell: bash
80+
working-directory: csharp
81+
run: |
82+
set -euo pipefail
83+
dotnet test Fory.sln -c Release --no-build
84+
85+
- name: Pack Apache.Fory
86+
shell: bash
87+
working-directory: csharp
88+
run: |
89+
set -euo pipefail
90+
rm -rf artifacts/nuget
91+
mkdir -p artifacts/nuget
92+
dotnet pack src/Fory/Fory.csproj \
93+
-c Release \
94+
--no-restore \
95+
-o artifacts/nuget \
96+
-p:ContinuousIntegrationBuild=true
97+
98+
- name: Verify packed artifacts
99+
shell: bash
100+
working-directory: csharp
101+
run: |
102+
set -euo pipefail
103+
VERSION="${{ github.ref_name }}"
104+
VERSION="${VERSION#v}"
105+
test -f "artifacts/nuget/Apache.Fory.$VERSION.nupkg"
106+
test -f "artifacts/nuget/Apache.Fory.$VERSION.snupkg"
107+
ls -l artifacts/nuget
108+
109+
- name: Exchange GitHub OIDC token for NuGet API key
110+
id: nuget-login
111+
shell: bash
112+
env:
113+
NUGET_AUDIENCE: https://www.nuget.org
114+
NUGET_TOKEN_SERVICE_URL: https://www.nuget.org/api/v2/token
115+
run: |
116+
set -euo pipefail
117+
if [[ -z "${ACTIONS_ID_TOKEN_REQUEST_TOKEN:-}" || -z "${ACTIONS_ID_TOKEN_REQUEST_URL:-}" ]]; then
118+
echo "GitHub OIDC token exchange is unavailable. Ensure the job has id-token: write permission." >&2
119+
exit 1
120+
fi
121+
python <<'PY'
122+
import json
123+
import os
124+
import urllib.error
125+
import urllib.parse
126+
import urllib.request
127+
128+
def fetch_json(url: str, headers: dict[str, str], data: bytes | None = None) -> dict:
129+
request = urllib.request.Request(url, data=data, headers=headers)
130+
try:
131+
with urllib.request.urlopen(request) as response:
132+
return json.load(response)
133+
except urllib.error.HTTPError as error:
134+
body = error.read().decode("utf-8", errors="replace")
135+
raise SystemExit(f"HTTP {error.code} from {url}: {body}") from error
136+
137+
request_token = os.environ["ACTIONS_ID_TOKEN_REQUEST_TOKEN"]
138+
request_url = os.environ["ACTIONS_ID_TOKEN_REQUEST_URL"]
139+
audience = os.environ["NUGET_AUDIENCE"]
140+
token_service_url = os.environ["NUGET_TOKEN_SERVICE_URL"]
141+
142+
print(f"::add-mask::{request_token}")
143+
144+
oidc_response = fetch_json(
145+
f"{request_url}&audience={urllib.parse.quote(audience, safe='')}",
146+
{"Authorization": f"Bearer {request_token}"},
147+
)
148+
oidc_token = oidc_response.get("value")
149+
if not oidc_token:
150+
raise SystemExit("GitHub OIDC response did not contain a token value.")
151+
print(f"::add-mask::{oidc_token}")
152+
153+
api_key_response = fetch_json(
154+
token_service_url,
155+
{
156+
"Authorization": f"Bearer {oidc_token}",
157+
"Content-Type": "application/json",
158+
"User-Agent": "apache-fory-release-csharp-workflow",
159+
},
160+
json.dumps({"username": "chaokunyang", "tokenType": "ApiKey"}).encode("utf-8"),
161+
)
162+
api_key = api_key_response.get("apiKey")
163+
if not api_key:
164+
raise SystemExit('NuGet token exchange response did not contain "apiKey".')
165+
print(f"::add-mask::{api_key}")
166+
167+
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output:
168+
output.write(f"NUGET_API_KEY={api_key}\n")
169+
PY
170+
171+
- name: Publish Apache.Fory package
172+
shell: bash
173+
working-directory: csharp
174+
run: |
175+
set -euo pipefail
176+
VERSION="${{ github.ref_name }}"
177+
VERSION="${VERSION#v}"
178+
dotnet nuget push "artifacts/nuget/Apache.Fory.$VERSION.nupkg" \
179+
--api-key "${{ steps.nuget-login.outputs.NUGET_API_KEY }}" \
180+
--source "$NUGET_SOURCE" \
181+
--skip-duplicate
182+
183+
- name: Publish Apache.Fory symbols
184+
shell: bash
185+
working-directory: csharp
186+
run: |
187+
set -euo pipefail
188+
VERSION="${{ github.ref_name }}"
189+
VERSION="${VERSION#v}"
190+
dotnet nuget push "artifacts/nuget/Apache.Fory.$VERSION.snupkg" \
191+
--api-key "${{ steps.nuget-login.outputs.NUGET_API_KEY }}" \
192+
--source "$NUGET_SOURCE" \
193+
--skip-duplicate
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
name: Publish Dart
19+
run-name: "Dart Release: ${{ github.ref_name }}"
20+
21+
on:
22+
push:
23+
tags: ['v*']
24+
25+
permissions:
26+
contents: read
27+
id-token: write
28+
29+
concurrency:
30+
group: release-dart-${{ github.ref }}
31+
cancel-in-progress: false
32+
33+
jobs:
34+
publish-dart:
35+
runs-on: ubuntu-latest
36+
if: github.repository == 'apache/fory'
37+
permissions:
38+
contents: read
39+
id-token: write
40+
steps:
41+
- uses: actions/checkout@v5
42+
43+
- uses: actions/setup-python@v6
44+
with:
45+
python-version: '3.11'
46+
cache: 'pip'
47+
48+
- name: Bump Dart package versions for the release tag
49+
shell: bash
50+
run: |
51+
set -euo pipefail
52+
VERSION="${{ github.ref_name }}"
53+
VERSION="${VERSION#v}"
54+
python ci/release.py bump_version -l dart -version "$VERSION"
55+
VERSION="$VERSION" ruby <<'RUBY'
56+
require "yaml"
57+
58+
expected = ENV.fetch("VERSION")
59+
files = [
60+
"dart/pubspec.yaml",
61+
"dart/packages/fory/pubspec.yaml",
62+
"dart/packages/fory-test/pubspec.yaml",
63+
]
64+
65+
files.each do |path|
66+
actual = YAML.load_file(path).fetch("version")
67+
if actual != expected
68+
abort("#{path} version mismatch after bump: expected #{expected}, got #{actual}")
69+
end
70+
end
71+
72+
puts "Verified bumped Dart package versions for #{expected}"
73+
RUBY
74+
75+
- uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c # v1.7.1
76+
77+
- name: Install Dart package dependencies
78+
shell: bash
79+
working-directory: dart/packages/fory
80+
run: |
81+
set -euo pipefail
82+
dart pub get
83+
84+
- name: Verify publish contents
85+
shell: bash
86+
working-directory: dart/packages/fory
87+
run: |
88+
set -euo pipefail
89+
dart pub publish --dry-run
90+
91+
- name: Publish to pub.dev
92+
shell: bash
93+
working-directory: dart/packages/fory
94+
run: |
95+
set -euo pipefail
96+
dart pub publish --force

ci/release.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,12 @@ def bump_dart_version(new_version):
293293
"dart/packages/fory-test",
294294
]:
295295
_bump_version(p, "pubspec.yaml", new_version, _update_pubspec_version)
296+
_bump_version(
297+
"dart/packages/fory",
298+
"README.md",
299+
new_version,
300+
_update_dart_readme_dependency_version,
301+
)
296302

297303

298304
def bump_compiler_version(new_version):
@@ -497,6 +503,14 @@ def _update_pubspec_version(lines, v: str):
497503
return lines
498504

499505

506+
def _update_dart_readme_dependency_version(lines, v: str):
507+
for index, line in enumerate(lines):
508+
if re.match(r"^\s*fory:\s*\^[^\s]+\s*$", line):
509+
lines[index] = f" fory: ^{v}\n"
510+
return lines
511+
raise ValueError("No Dart README dependency snippet for fory found")
512+
513+
500514
def _update_csharp_props_version(lines, v: str):
501515
for index, line in enumerate(lines):
502516
if "<Version>" not in line:

0 commit comments

Comments
 (0)