3 Commits
4 changed files with 215 additions and 3 deletions

No files matched your search

@@ -0,0 +1,93 @@
name: daily-avalonia-native
on:
schedule:
- cron: '17 3 * * *'
workflow_dispatch:
inputs:
force_build:
description: Rebuild even if matching successful Actions artifacts exist
type: boolean
default: false
permissions:
contents: read
actions: write
concurrency:
group: daily-avalonia-native
cancel-in-progress: false
jobs:
check:
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
major: [11, 12]
steps:
- name: Check latest stable Avalonia and dispatch Native build
env:
GH_TOKEN: ${{ github.token }}
AVALONIA_MAJOR: ${{ matrix.major }}
FORCE_BUILD: ${{ inputs.force_build }}
NATIVE_BRANCH: avalonia-native
PACKAGE_REVISION: '2'
run: |
python3 - <<'PY'
import json
import os
import re
import subprocess
repository = os.environ['GITHUB_REPOSITORY']
branch = os.environ['NATIVE_BRANCH']
major = int(os.environ['AVALONIA_MAJOR'])
def api(endpoint):
return json.loads(subprocess.check_output(['gh', 'api', endpoint], text=True))
def summary(message):
print(message, flush=True)
with open(os.environ['GITHUB_STEP_SUMMARY'], 'a', encoding='utf-8') as output:
output.write(message + '\n')
upstream = json.loads(subprocess.check_output([
'curl', '--fail', '--silent', '--show-error', '--location', '--retry', '3',
'https://api.nuget.org/v3-flatcontainer/avalonia/index.json',
], text=True))
candidates = [version for version in upstream['versions']
if re.fullmatch(r'\d+\.\d+\.\d+', version)
and int(version.split('.')[0]) == major]
if not candidates:
raise SystemExit(f'No stable Avalonia {major} version found')
version = max(candidates, key=lambda value: tuple(map(int, value.split('.'))))
package_version = version + '.' + os.environ['PACKAGE_REVISION']
artifact_name = 'staticlink-avalonia-native-' + package_version
head_sha = api(f'repos/{repository}/branches/{branch}')['commit']['sha']
if os.environ['FORCE_BUILD'] != 'true':
already_validated = False
runs = api(f'repos/{repository}/actions/workflows/nuget-avalonia-native.yml/runs'
f'?branch={branch}&status=success&per_page=100')['workflow_runs']
for run in runs:
if run['head_sha'] != head_sha or run['conclusion'] != 'success':
continue
artifacts = api(f"repos/{repository}/actions/runs/{run['id']}/artifacts?per_page=100")['artifacts']
if any(artifact['name'] == artifact_name and not artifact['expired']
for artifact in artifacts):
summary(f"Already validated {package_version}: {run['html_url']}")
already_validated = True
break
if already_validated:
raise SystemExit(0)
payload = json.dumps({'ref': branch, 'inputs': {
'avalonia_version': version, 'force_build': True, 'publish_to_nuget': False,
}})
subprocess.run([
'gh', 'api', '--method', 'POST',
f'repos/{repository}/actions/workflows/nuget-avalonia-native.yml/dispatches',
'--input', '-',
], input=payload, text=True, check=True)
summary(f'Dispatched {package_version} on {branch}; Actions artifacts only, no NuGet publication.')
PY
@@ -0,0 +1,76 @@
name: daily-native-release
on:
workflow_dispatch:
inputs:
force_build:
description: Build current stable versions even if already published
type: boolean
default: false
permissions:
contents: read
actions: write
concurrency:
group: daily-native-release-${{ github.ref }}
cancel-in-progress: false
jobs:
dispatch:
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
branch: [skia3, skia4, avalonia-native]
steps:
- uses: actions/checkout@v4
with:
ref: ${{ matrix.branch }}
- name: Check and dispatch this branch
env:
GH_TOKEN: ${{ github.token }}
TRACK_BRANCH: ${{ matrix.branch }}
FORCE_BUILD: ${{ inputs.force_build }}
run: |
python3 - <<'PY'
import json
import os
import subprocess
branch = os.environ["TRACK_BRANCH"]
force = os.environ["FORCE_BUILD"] == "true"
def dispatch(workflow, inputs):
payload = json.dumps({"ref": branch, "inputs": inputs})
subprocess.run(
["gh", "api", "--method", "POST",
f"repos/{os.environ['GITHUB_REPOSITORY']}/actions/workflows/{workflow}/dispatches",
"--input", "-"],
input=payload, text=True, check=True,
)
if branch == "avalonia-native":
dispatch("nuget-avalonia-native.yml", {
"force_build": force, "publish_to_nuget": False,
})
else:
command = ["python3", "scripts/check-upstream-releases.py"]
if force:
command.append("--force")
subprocess.run(command, check=True)
for release in json.load(open("release-plan.json")):
if release["ref"] != branch:
raise SystemExit("Release track does not match the checked-out branch")
dispatch("nuget-static-graphics.yml", {
"skiasharp_version": release["skiasharp_version"],
"publish_to_nuget": False, "publish_to_release": False,
})
print(f"Dispatched {release['package_version']} on {branch}")
PY
- uses: actions/upload-artifact@v4
if: ${{ always() && matrix.branch != 'avalonia-native' }}
with:
name: daily-release-plan-${{ matrix.branch }}
path: release-plan.json
if-no-files-found: warn
+1 -3
View File
@@ -1,8 +1,6 @@
name: staticlink-avalonia-native
on:
schedule:
- cron: "17 3 * * *"
workflow_dispatch:
inputs:
avalonia_version:
@@ -17,7 +15,7 @@ on:
publish_to_nuget:
description: Publish with NuGet Trusted Publishing after smoke tests
required: true
default: true
default: false
type: boolean
jobs:
+45
View File
@@ -0,0 +1,45 @@
# Manual artifact validation
Native daily checks are enabled by daily-avalonia-native.yml on main at 03:17
UTC (11:17 Singapore time). Skia3/4 daily schedules remain disabled. No
NuGet.org publication or GitHub Release is requested by either dispatcher.
The Native checker handles the latest stable Avalonia 11 and 12 independently.
It skips versions with non-expired NuGet artifacts from a successful Native
run at the current avalonia-native branch commit. New versions, changed Native
code, expired artifacts, or failed builds trigger a new artifact-only build.
Set force_build=true on the daily checker to bypass this successful-run check.
Before release builds, run nuget-static-graphics.yml on skia4 with
environment_only=true. This checks the actual container's Python/depot_tools,
.NET 8/10, SDKs, sysroots, and C/C++ compilation plus C executable linking for
all nine RIDs. Download toolchain-environment-validation to inspect the report.
The same environment gate runs before every graphics and native build and
must pass before the toolchain image is published for release compilation.
- skia3: nuget-static-graphics.yml; latest stable SkiaSharp 3.
- skia4: nuget-static-graphics.yml; latest stable SkiaSharp 4.
- avalonia-native: nuget-avalonia-native.yml; latest stable Avalonia 11 and 12.
Download staticlink-avalonia-nuget and avalonia-smoke-<RID> from each graphics run.
Download staticlink-avalonia-native-<version> and
avalonia-native-smoke-<AvaloniaVersion>-<RID> from the native run.
Native archives and source locks are also retained as Actions artifacts.
Graphics macOS smoke tests use the already published AvaloniaNative revision 1
by default, so the graphics builds do not require the experimental native
revision 2 to be published. The native branch validates its own local revision 2
package separately. To test both experimental packages together, put both
downloaded packages in a local NuGet feed and reference their exact versions.
Smoke executables are retained even if a later runtime validation fails.
An artifact is not evidence that all validation gates passed; check job results.
The daily-native-release workflow on main currently supports manual dispatch
only. Its three matrix jobs check out their own branch and dispatch independently.
Use force_build=true to rebuild published versions for testing.
After validation, enable the schedule on main only; non-default branches cannot
run their own GitHub cron schedules. Publication remains an explicit opt-in.
The manual graphics dispatcher still checks publication rather than successful
Actions runs. Add the same successful-run tracking before enabling artifact-only
daily graphics builds if avoiding repeated builds is required.