mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Matrix in CI and mach try
with presets (#31141)
* Matrix in CI and mach try with presets * small fixups * names in trigger try run comment * let * f * rename step * fix running try on win * fix try branch full * py3.10 * typo * Make unit-tests default to false, except in basic os runs Fixes https://github.com/servo/servo/issues/31174 * make full use linux-wpt & linux-wpt also include unit-tests so full is equal to main workflow * Stylish fixes * cmp json as dict
This commit is contained in:
parent
266a082206
commit
a5c512808a
9 changed files with 582 additions and 322 deletions
4
.github/workflows/android.yml
vendored
4
.github/workflows/android.yml
vendored
|
@ -11,10 +11,8 @@ on:
|
|||
profile:
|
||||
required: false
|
||||
default: "release"
|
||||
type: string
|
||||
type: choice
|
||||
options: ["release", "debug", "production"]
|
||||
push:
|
||||
branches: ["try-android"]
|
||||
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
|
|
55
.github/workflows/dispatch-workflow.yml
vendored
Normal file
55
.github/workflows/dispatch-workflow.yml
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
name: Dispatch Workflow
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
workflow:
|
||||
required: true
|
||||
type: string
|
||||
profile:
|
||||
required: true
|
||||
type: string
|
||||
wpt-tests-to-run:
|
||||
required: true
|
||||
type: string
|
||||
wpt-layout:
|
||||
required: true
|
||||
type: string
|
||||
unit-tests:
|
||||
required: true
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
win:
|
||||
if: ${{ inputs.workflow == 'windows' }}
|
||||
uses: ./.github/workflows/windows.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
profile: ${{ inputs.profile }}
|
||||
unit-tests: ${{ inputs.unit-tests }}
|
||||
|
||||
macos:
|
||||
if: ${{ inputs.workflow == 'macos' }}
|
||||
uses: ./.github/workflows/mac.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
profile: ${{ inputs.profile }}
|
||||
wpt-layout: ${{ inputs.wpt-layout }}
|
||||
unit-tests: ${{ inputs.unit-tests }}
|
||||
wpt-tests-to-run: ${{ inputs.wpt-tests-to-run }}
|
||||
|
||||
linux:
|
||||
if: ${{ inputs.workflow == 'linux' }}
|
||||
uses: ./.github/workflows/linux.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
profile: ${{ inputs.profile }}
|
||||
wpt-layout: ${{ inputs.wpt-layout }}
|
||||
unit-tests: ${{ inputs.unit-tests }}
|
||||
wpt-tests-to-run: ${{ inputs.wpt-tests-to-run }}
|
||||
|
||||
android:
|
||||
if: ${{ inputs.workflow == 'android' }}
|
||||
uses: ./.github/workflows/android.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
profile: ${{ inputs.profile }}
|
106
.github/workflows/main.yml
vendored
106
.github/workflows/main.yml
vendored
|
@ -11,124 +11,36 @@ on:
|
|||
branches: ["**"]
|
||||
merge_group:
|
||||
types: [checks_requested]
|
||||
workflow_call:
|
||||
inputs:
|
||||
configuration:
|
||||
required: true
|
||||
type: string
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
profile:
|
||||
required: false
|
||||
default: "release"
|
||||
type: choice
|
||||
options: ["release", "debug", "production"]
|
||||
wpt-tests-to-run:
|
||||
default: ""
|
||||
required: false
|
||||
type: string
|
||||
platform:
|
||||
required: false
|
||||
type: choice
|
||||
options: ["linux", "windows", "macos", "all"]
|
||||
wpt-layout:
|
||||
required: false
|
||||
type: choice
|
||||
options: ["none", "2013", "2020", "all"]
|
||||
unit-tests:
|
||||
required: false
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
decision:
|
||||
name: Decision
|
||||
runs-on: ubuntu-20.04
|
||||
outputs:
|
||||
configuration: ${{ steps.configuration.outputs.result }}
|
||||
steps:
|
||||
- name: Configuration
|
||||
id: configuration
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
// If this is a workflow call with a configuration object,
|
||||
// then just return it immediately.
|
||||
let configuration = ${{ inputs.configuration || '""' }};
|
||||
if (configuration != "") {
|
||||
console.log("Using configuration: " + JSON.stringify(configuration));
|
||||
return configuration;
|
||||
}
|
||||
|
||||
// We need to pick defaults if the inputs are not provided. Unprovided inputs
|
||||
// are empty strings in this template.
|
||||
let platform = "${{ inputs.platform }}" || "linux";
|
||||
let profile = "${{ inputs.profile }}" || "release";
|
||||
let wpt_layout = "${{ inputs.wpt-layout }}" || "none";
|
||||
let wpt_tests_to_run = "${{ inputs.wpt-tests-to-run }}" || "";
|
||||
let unit_tests = Boolean(${{ inputs.unit-tests }})
|
||||
|
||||
// Merge queue runs and pushes to `main` should always trigger a full build and test.
|
||||
if (["push", "merge_group"].includes(context.eventName)) {
|
||||
platform = "all";
|
||||
wpt_layout = "all";
|
||||
unit_tests = true;
|
||||
}
|
||||
|
||||
let platforms = [];
|
||||
if (platform == "all") {
|
||||
platforms = [ "linux", "windows", "macos", "android" ];
|
||||
} else {
|
||||
platforms = [ platform ];
|
||||
}
|
||||
|
||||
let returnValue = {
|
||||
platforms,
|
||||
wpt_layout,
|
||||
unit_tests,
|
||||
profile,
|
||||
wpt_tests_to_run,
|
||||
};
|
||||
|
||||
console.log("Using configuration: " + JSON.stringify(returnValue));
|
||||
return returnValue;
|
||||
|
||||
build-win:
|
||||
name: Windows
|
||||
needs: ["decision"]
|
||||
if: ${{ contains(fromJson(needs.decision.outputs.configuration).platforms, 'windows') }}
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
uses: ./.github/workflows/windows.yml
|
||||
with:
|
||||
profile: ${{ fromJson(needs.decision.outputs.configuration).profile }}
|
||||
unit-tests: ${{ fromJson(needs.decision.outputs.configuration).unit_tests }}
|
||||
unit-tests: true
|
||||
secrets: inherit
|
||||
|
||||
build-mac:
|
||||
name: Mac
|
||||
needs: ["decision"]
|
||||
if: ${{ contains(fromJson(needs.decision.outputs.configuration).platforms, 'macos') }}
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
uses: ./.github/workflows/mac.yml
|
||||
with:
|
||||
wpt-tests-to-run: ${{ fromJson(needs.decision.outputs.configuration).wpt_tests_to_run }}
|
||||
profile: ${{ fromJson(needs.decision.outputs.configuration).profile }}
|
||||
unit-tests: ${{ fromJson(needs.decision.outputs.configuration).unit_tests }}
|
||||
unit-tests: true
|
||||
secrets: inherit
|
||||
|
||||
build-linux:
|
||||
name: Linux
|
||||
needs: ["decision"]
|
||||
if: ${{ contains(fromJson(needs.decision.outputs.configuration).platforms, 'linux') }}
|
||||
uses: ./.github/workflows/linux.yml
|
||||
with:
|
||||
wpt-tests-to-run: ${{ fromJson(needs.decision.outputs.configuration).wpt_tests_to_run }}
|
||||
profile: ${{ fromJson(needs.decision.outputs.configuration).profile }}
|
||||
wpt-layout: ${{ fromJson(needs.decision.outputs.configuration).wpt_layout }}
|
||||
unit-tests: ${{ fromJson(needs.decision.outputs.configuration).unit_tests }}
|
||||
unit-tests: true
|
||||
wpt-layout: ${{ github.event_name == 'pull_request' && 'none' || 'all' }}
|
||||
secrets: inherit
|
||||
|
||||
build-android:
|
||||
name: Android
|
||||
needs: ["decision"]
|
||||
if: ${{ contains(fromJson(needs.decision.outputs.configuration).platforms, 'android') }}
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
uses: ./.github/workflows/android.yml
|
||||
with:
|
||||
profile: "release"
|
||||
|
@ -140,15 +52,11 @@ jobs:
|
|||
if: always()
|
||||
# needs all build to detect cancellation
|
||||
needs:
|
||||
- "decision"
|
||||
- "build-win"
|
||||
- "build-mac"
|
||||
- "build-linux"
|
||||
- "build-android"
|
||||
steps:
|
||||
- name: Mark skipped jobs as successful
|
||||
if: ${{ fromJson(needs.decision.outputs.configuration).platforms[0] != null }}
|
||||
run: exit 0
|
||||
- name: Mark the job as successful
|
||||
if: ${{ !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }}
|
||||
run: exit 0
|
||||
|
|
270
.github/workflows/try.yml
vendored
270
.github/workflows/try.yml
vendored
|
@ -1,203 +1,123 @@
|
|||
name: Try
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [labeled]
|
||||
push:
|
||||
branches: ["try", "try-linux", "try-mac", "try-wpt-mac", "try-wpt-mac-2020", "try-wpt", "try-wpt-2020", "try-windows"]
|
||||
branches: ["try"]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
profile:
|
||||
required: false
|
||||
default: "release"
|
||||
type: choice
|
||||
options: ["release", "debug", "production"]
|
||||
wpt-tests-to-run:
|
||||
default: ""
|
||||
required: false
|
||||
type: string
|
||||
wpt-layout:
|
||||
required: false
|
||||
type: choice
|
||||
options: ["none", "2013", "2020", "all"]
|
||||
unit-tests:
|
||||
required: false
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
parse-comment:
|
||||
name: Trigger Try
|
||||
runs-on: ubuntu-latest
|
||||
concurrency:
|
||||
group: try-${{ github.event.number }}
|
||||
decision:
|
||||
name: Generate Try Configuration
|
||||
runs-on: ubuntu-20.04
|
||||
outputs:
|
||||
configuration: ${{ steps.configuration.outputs.result }}
|
||||
steps:
|
||||
- uses: actions/github-script@v6
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 1
|
||||
sparse-checkout: |
|
||||
python/servo/try_parser.py
|
||||
sparse-checkout-cone-mode: false
|
||||
- name: Get Full Configuration
|
||||
id: full_config
|
||||
run: |
|
||||
{
|
||||
echo 'config<<EOF'
|
||||
python ./python/servo/try_parser.py
|
||||
echo EOF
|
||||
} >> $GITHUB_OUTPUT
|
||||
- name: Configuration
|
||||
id: configuration
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
function makeComment(body) {
|
||||
console.log(body);
|
||||
// When triggered via a push try the `try` branch, search the last commit for a configuration object.
|
||||
if (${{ github.ref_name == 'try' }}) {
|
||||
let commit_msg = context.payload.head_commit.message;
|
||||
try {
|
||||
var config = JSON.parse(commit_msg.split('\n').slice(-1));
|
||||
|
||||
if (context.eventName != 'pull_request_target')
|
||||
return;
|
||||
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body
|
||||
})
|
||||
}
|
||||
|
||||
function combineWPTLayoutOptions(layout, newLayout) {
|
||||
let has2013 = layout == "2013" || layout == "all";
|
||||
let has2020 = layout == "2020" || layout == "all";
|
||||
let adding2013 = newLayout == "2013";
|
||||
let adding2020 = newLayout == "2020";
|
||||
|
||||
if ((adding2020 && has2020) || (adding2013 && has2013)) {
|
||||
return layout;
|
||||
if (config && typeof config === "object") {
|
||||
console.log("Using try commit configuration: " + JSON.stringify(config));
|
||||
return config;
|
||||
}
|
||||
if (adding2020) {
|
||||
return has2013 ? "all" : "2020";
|
||||
}
|
||||
if (adding2013) {
|
||||
return has2020 ? "all" : "2013";
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
|
||||
function addPlatformToConfiguration(platform, configuration) {
|
||||
if (!configuration.platforms.includes(platform)) {
|
||||
configuration.platforms.push(platform);
|
||||
}
|
||||
catch (exception) {
|
||||
console.log("Could not parse try configuration from commit message: " + exception);
|
||||
console.log("Triggering full try run.");
|
||||
}
|
||||
}
|
||||
|
||||
function updateConfigurationFromString(tryString, configuration) {
|
||||
if (tryString.includes("full")) {
|
||||
configuration.platforms = ["linux", "macos", "windows"];
|
||||
configuration.unit_tests = true;
|
||||
configuration.wpt_layout = "all";
|
||||
return configuration;
|
||||
}
|
||||
// If we reach here we are likely doing a full run.
|
||||
configuration = ${{ steps.full_config.outputs.config }};
|
||||
|
||||
if (tryString.includes("linux")) {
|
||||
addPlatformToConfiguration("linux", configuration);
|
||||
configuration.unit_tests = true;
|
||||
} else if (tryString.includes("mac")) {
|
||||
addPlatformToConfiguration("macos", configuration);
|
||||
configuration.unit_tests = true;
|
||||
} else if (tryString.includes("win")) {
|
||||
addPlatformToConfiguration("windows", configuration);
|
||||
configuration.unit_tests = true;
|
||||
}
|
||||
// Process `workflow_dispatch` provided configuration overrides.
|
||||
if (context.eventName == "workflow_dispatch") {
|
||||
// WPT-related overrides only affect Linux currently, as tests don't run by default on other platforms.
|
||||
configuration.matrix[0].wpt_layout = "${{ inputs.wpt-layout }}" || "none";
|
||||
configuration.matrix[0].wpt_tests_to_run = "${{ inputs.wpt-tests-to-run }}" || "";
|
||||
|
||||
if (tryString.includes("wpt")) {
|
||||
addPlatformToConfiguration("linux", configuration);
|
||||
if (tryString.includes("2020")) {
|
||||
configuration.wpt_layout = combineWPTLayoutOptions(configuration.wpt_layout, "2020");
|
||||
} else {
|
||||
configuration.wpt_layout = combineWPTLayoutOptions(configuration.wpt_layout, "2013");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let configuration = {
|
||||
platforms: [],
|
||||
wpt_layout: "none",
|
||||
unit_tests: false,
|
||||
profile: "release",
|
||||
wpt_tests_to_run: "",
|
||||
};
|
||||
|
||||
if (context.eventName == 'pull_request_target') {
|
||||
for (const label of context.payload.pull_request.labels) {
|
||||
if (!label.name.startsWith("T-")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to remove the label. If that fails, it's likely that another
|
||||
// workflow has already processed it or a user has removed it.
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
name: label.name,
|
||||
});
|
||||
} catch (exception) {
|
||||
console.log("Assuming '" + label.name + "' is already removed: " + exception);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log("Found label: " + label.name);
|
||||
updateConfigurationFromString(label.name, configuration);
|
||||
}
|
||||
} else {
|
||||
let ref_name = "${{ github.ref_name || 'empty' }}";
|
||||
if (ref_name == "try") {
|
||||
updateConfigurationFromString("full", configuration);
|
||||
} else {
|
||||
updateConfigurationFromString(ref_name, configuration);
|
||||
let unit_tests = Boolean(${{ inputs.unit-tests }});
|
||||
let profile = '${{ inputs.profile }}';
|
||||
for (const platform of configuration.matrix) {
|
||||
platform.profile = profile;
|
||||
platform.unit_tests = unit_tests;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(JSON.stringify(configuration));
|
||||
|
||||
if (configuration.platforms.length == 0) {
|
||||
return { platforms: [] };
|
||||
}
|
||||
|
||||
let username = context.payload.sender.login;
|
||||
let result = await github.rest.repos.getCollaboratorPermissionLevel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
username
|
||||
});
|
||||
if (!result.data.user.permissions.push) {
|
||||
makeComment('🔒 User @' + username + ' does not have permission to trigger try jobs.');
|
||||
return { platforms: [] };
|
||||
}
|
||||
|
||||
const url = context.serverUrl +
|
||||
"/" + context.repo.owner +
|
||||
"/" + context.repo.repo +
|
||||
"/actions/runs/" + context.runId;
|
||||
const formattedURL = "[#" + context.runId + "](" + url + ")";
|
||||
let platformsString = configuration.platforms.toString();
|
||||
makeComment("🔨 Triggering try run (" + formattedURL + ") with platforms=" +
|
||||
platformsString + " and layout=" + configuration.wpt_layout);
|
||||
console.log("Using configuration: " + JSON.stringify(configuration));
|
||||
return configuration;
|
||||
|
||||
run-try:
|
||||
name: Run Try
|
||||
needs: ["parse-comment"]
|
||||
if: ${{ fromJson(needs.parse-comment.outputs.configuration).platforms[0] != null }}
|
||||
uses: ./.github/workflows/main.yml
|
||||
build:
|
||||
needs: ["decision"]
|
||||
name: ${{ matrix.name }}
|
||||
strategy:
|
||||
fail-fast: ${{ fromJson(needs.decision.outputs.configuration).fail_fast }}
|
||||
matrix:
|
||||
include: ${{ fromJson(needs.decision.outputs.configuration).matrix }}
|
||||
# We need to use `dipatch-workflow.yml` because workflows do not support using: ${}
|
||||
uses: ./.github/workflows/dispatch-workflow.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
configuration: ${{ needs.parse-comment.outputs.configuration }}
|
||||
workflow: ${{ matrix.workflow }}
|
||||
wpt-layout: ${{ matrix.wpt_layout }}
|
||||
profile: ${{ matrix.profile }}
|
||||
unit-tests: ${{ matrix.unit_tests }}
|
||||
wpt-tests-to-run: ${{ matrix.wpt_tests_to_run }}
|
||||
|
||||
results:
|
||||
name: Results
|
||||
needs: ["parse-comment", "run-try"]
|
||||
build-result:
|
||||
name: Result
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ always() && fromJson(needs.parse-comment.outputs.configuration).platforms[0] != null }}
|
||||
if: always()
|
||||
# `needs: "build"` is necessary to detect cancellation.
|
||||
needs:
|
||||
- "decision"
|
||||
- "build"
|
||||
|
||||
steps:
|
||||
- name: Success
|
||||
if: ${{ github.event_name == 'pull_request_target' && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }}
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const url = context.serverUrl +
|
||||
"/" + context.repo.owner +
|
||||
"/" + context.repo.repo +
|
||||
"/actions/runs/" + context.runId;
|
||||
const formattedURL = "[#" + context.runId + "](" + url + ")";
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: "✨ Try run (" + formattedURL + ") " + "succeeded.",
|
||||
});
|
||||
- name: Failure
|
||||
if: ${{ github.event_name == 'pull_request_target' && contains(needs.*.result, 'failure') }}
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const url = context.serverUrl +
|
||||
"/" + context.repo.owner +
|
||||
"/" + context.repo.repo +
|
||||
"/actions/runs/" + context.runId;
|
||||
const formattedURL = "[#" + context.runId + "](" + url + ")";
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: "⚠️ Try run (" + formattedURL + ") " + "failed.",
|
||||
});
|
||||
|
||||
|
||||
- name: Mark the job as successful
|
||||
if: ${{ !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }}
|
||||
run: exit 0
|
||||
- name: Mark the job as unsuccessful
|
||||
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
|
||||
run: exit 1
|
||||
|
|
184
.github/workflows/try_labels.yml
vendored
Normal file
184
.github/workflows/try_labels.yml
vendored
Normal file
|
@ -0,0 +1,184 @@
|
|||
name: Try (Label)
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [labeled]
|
||||
|
||||
jobs:
|
||||
parse-comment:
|
||||
name: Trigger Try
|
||||
runs-on: ubuntu-latest
|
||||
concurrency:
|
||||
group: try-${{ github.event.number }}
|
||||
outputs:
|
||||
configuration: ${{ steps.configuration.outputs.config }}
|
||||
try_string: ${{ steps.try_string.outputs.result }}
|
||||
steps:
|
||||
- name: Collect Labels
|
||||
uses: actions/github-script@v6
|
||||
id: try_string
|
||||
with:
|
||||
result-encoding: string
|
||||
script: |
|
||||
function makeComment(body) {
|
||||
console.log(body);
|
||||
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body
|
||||
})
|
||||
}
|
||||
|
||||
let try_string = "";
|
||||
|
||||
for (let label of context.payload.pull_request.labels) {
|
||||
if (!label.name.startsWith("T-")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to remove the label. If that fails, it's likely that another
|
||||
// workflow has already processed it or a user has removed it.
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
name: label.name,
|
||||
});
|
||||
} catch (exception) {
|
||||
console.log("Assuming '" + label.name + "' is already removed: " + exception);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log("Found label: " + label.name);
|
||||
// Remove the "T-" prefix.
|
||||
label = label.name.slice(2);
|
||||
try_string += " " + label;
|
||||
}
|
||||
|
||||
console.log(try_string);
|
||||
|
||||
// Exit early if the try string is empty (no try triggered).
|
||||
if (!try_string.trim()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
let username = context.payload.sender.login;
|
||||
let result = await github.rest.repos.getCollaboratorPermissionLevel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
username
|
||||
});
|
||||
if (!result.data.user.permissions.push) {
|
||||
makeComment('🔒 User @' + username + ' does not have permission to trigger try jobs.');
|
||||
return "";
|
||||
}
|
||||
|
||||
return try_string;
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
# This is necessary to checkout the pull request if this run was triggered
|
||||
# via an `label` action on a pull request.
|
||||
ref: refs/pull/${{ github.event.number }}/head
|
||||
fetch-depth: 1
|
||||
sparse-checkout: |
|
||||
python/servo/try_parser.py
|
||||
sparse-checkout-cone-mode: false
|
||||
- name: Parse Labels
|
||||
if: ${{ steps.try_string.outputs.result }}
|
||||
id: configuration
|
||||
run: |
|
||||
{
|
||||
echo 'config<<EOF'
|
||||
python ./python/servo/try_parser.py ${{ steps.try_string.outputs.result }}
|
||||
echo EOF
|
||||
} >> $GITHUB_OUTPUT
|
||||
- name: Comment Run Start
|
||||
if: ${{ steps.try_string.outputs.result }}
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
result-encoding: string
|
||||
script: |
|
||||
let config = ${{ steps.configuration.outputs.config }};
|
||||
function makeComment(body) {
|
||||
console.log(body);
|
||||
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body
|
||||
})
|
||||
}
|
||||
|
||||
const url = context.serverUrl +
|
||||
"/" + context.repo.owner +
|
||||
"/" + context.repo.repo +
|
||||
"/actions/runs/" + context.runId;
|
||||
const formattedURL = "[#" + context.runId + "](" + url + ")";
|
||||
makeComment("🔨 Triggering try run (" + formattedURL + ") for "
|
||||
+ config.matrix.map(m => m.name).join(", "));
|
||||
|
||||
run-try:
|
||||
if: ${{ needs.parse-comment.outputs.try_string }}
|
||||
needs: ["parse-comment"]
|
||||
name: ${{ matrix.name }}
|
||||
strategy:
|
||||
fail-fast: ${{ fromJson(needs.parse-comment.outputs.configuration).fail_fast }}
|
||||
matrix:
|
||||
include: ${{ fromJson(needs.parse-comment.outputs.configuration).matrix }}
|
||||
# We need to use `dipatch-workflow.yml` because workflows do not support using: ${}.
|
||||
uses: ./.github/workflows/dispatch-workflow.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
workflow: ${{ matrix.workflow }}
|
||||
wpt-layout: ${{ matrix.wpt_layout }}
|
||||
profile: ${{ matrix.profile }}
|
||||
unit-tests: ${{ matrix.unit_tests }}
|
||||
wpt-tests-to-run: ${{ matrix.wpt_tests_to_run }}
|
||||
|
||||
results:
|
||||
name: Results
|
||||
needs: ["parse-comment", "run-try"]
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ always() && needs.parse-comment.outputs.try_string }}
|
||||
steps:
|
||||
- name: Success
|
||||
if: ${{ !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }}
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const url = context.serverUrl +
|
||||
"/" + context.repo.owner +
|
||||
"/" + context.repo.repo +
|
||||
"/actions/runs/" + context.runId;
|
||||
const formattedURL = "[#" + context.runId + "](" + url + ")";
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: "✨ Try run (" + formattedURL + ") " + "succeeded.",
|
||||
});
|
||||
- name: Failure
|
||||
if: ${{ contains(needs.*.result, 'failure') }}
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const url = context.serverUrl +
|
||||
"/" + context.repo.owner +
|
||||
"/" + context.repo.repo +
|
||||
"/actions/runs/" + context.runId;
|
||||
const formattedURL = "[#" + context.runId + "](" + url + ")";
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: "⚠️ Try run (" + formattedURL + ") " + "failed.",
|
||||
});
|
||||
|
||||
|
3
.github/workflows/windows.yml
vendored
3
.github/workflows/windows.yml
vendored
|
@ -4,7 +4,8 @@ on:
|
|||
workflow_call:
|
||||
inputs:
|
||||
profile:
|
||||
required: true
|
||||
required: false
|
||||
default: "release"
|
||||
type: string
|
||||
unit-tests:
|
||||
required: false
|
||||
|
|
|
@ -22,17 +22,7 @@ from mach.decorators import (
|
|||
)
|
||||
|
||||
from servo.command_base import CommandBase, cd, call
|
||||
|
||||
VALID_TRY_BRACHES = [
|
||||
"try",
|
||||
"try-linux",
|
||||
"try-mac",
|
||||
"try-windows",
|
||||
"try-wpt",
|
||||
"try-wpt-2020",
|
||||
"try-wpt-mac",
|
||||
"try-wpt-mac-2020"
|
||||
]
|
||||
from servo.try_parser import Config
|
||||
|
||||
|
||||
@CommandProvider
|
||||
|
@ -284,38 +274,35 @@ class MachCommands(CommandBase):
|
|||
return p.wait()
|
||||
|
||||
@Command('try',
|
||||
description='Runs try jobs by force pushing to personal fork try branches',
|
||||
description='Runs try jobs by force pushing to try branch',
|
||||
category='devenv')
|
||||
@CommandArgument(
|
||||
'jobs', default=["try"], nargs='...',
|
||||
help="Name(s) of job(s) (ex: try, linux, mac, windows, wpt)")
|
||||
def try_jobs(self, jobs):
|
||||
branches = []
|
||||
# we validate branches because force pushing is destructive
|
||||
for job in jobs:
|
||||
# branches must start with try-
|
||||
if "try" not in job:
|
||||
job = "try-" + job
|
||||
if job not in VALID_TRY_BRACHES:
|
||||
print(job + " job doesn't exist")
|
||||
return -1
|
||||
branches.append(job)
|
||||
remote = "origin"
|
||||
if "servo/servo" in subprocess.check_output(["git", "config", "--get", "remote.origin.url"]).decode():
|
||||
# if we have servo/servo for origin check try remote
|
||||
try:
|
||||
if "servo/servo" in subprocess.check_output(["git", "config", "--get", "remote.try.url"]).decode():
|
||||
# User has servo/servo for try remote
|
||||
print("You should not use servo/servo for try remote!")
|
||||
return -1
|
||||
else:
|
||||
remote = "try"
|
||||
except subprocess.CalledProcessError:
|
||||
print("It looks like you are patching in upstream servo.")
|
||||
print("Set try remote to your personal fork with `git remote add try https://github.com/user/servo`")
|
||||
return -1
|
||||
for b in branches:
|
||||
res = call(["git", "push", remote, "--force", f"HEAD:{b}"], env=self.build_env())
|
||||
if res != 0:
|
||||
return res
|
||||
return 0
|
||||
'--remote', '-r', default="origin",
|
||||
help='git remote to use for try pushes')
|
||||
@CommandArgument(
|
||||
'try_string', default=None, nargs='...',
|
||||
help="Try string")
|
||||
def try_jobs(self, remote="origin", try_string=None):
|
||||
if not try_string:
|
||||
try_string = "full"
|
||||
else:
|
||||
try_string = " ".join(try_string)
|
||||
conf = Config(try_string)
|
||||
result = call(["git", "commit", "--allow-empty", "-m", try_string, "-m", f"{conf.to_json()}"])
|
||||
if result != 0:
|
||||
return result
|
||||
|
||||
git_remote = subprocess.check_output(["git", "config", "--get", f"remote.{remote}.url"]).decode()
|
||||
if "servo/servo" in git_remote:
|
||||
print("WARNING: You are triggering try build in upstream repo!")
|
||||
|
||||
result = call(["git", "push", remote, "--force", "HEAD:try"])
|
||||
if result != 0:
|
||||
return result
|
||||
|
||||
git_remote = git_remote.replace(".git", "/actions")
|
||||
print(f"You can find triggered workflow here: {git_remote}")
|
||||
|
||||
# Remove the last commit which only contains the try configuration.
|
||||
result += call(["git", "reset", "--soft", "HEAD~1"])
|
||||
return result
|
||||
|
|
|
@ -244,6 +244,10 @@ class MachCommands(CommandBase):
|
|||
print("Running tidy tests...")
|
||||
passed = tidy.run_tests() and passed
|
||||
|
||||
import python.servo.try_parser as try_parser
|
||||
print("Running try_parser tests...")
|
||||
passed = try_parser.run_tests() and passed
|
||||
|
||||
print("Running WPT tests...")
|
||||
passed = wpt.run_tests() and passed
|
||||
|
||||
|
|
203
python/servo/try_parser.py
Normal file
203
python/servo/try_parser.py
Normal file
|
@ -0,0 +1,203 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2023 The Servo Project Developers. See the COPYRIGHT
|
||||
# file at the top-level directory of this distribution.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
# option. This file may not be copied, modified, or distributed
|
||||
# except according to those terms.
|
||||
|
||||
import json
|
||||
import sys
|
||||
from typing import Optional
|
||||
import unittest
|
||||
import logging
|
||||
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum, Flag, auto
|
||||
|
||||
|
||||
class Layout(Flag):
|
||||
none = 0
|
||||
layout2013 = auto()
|
||||
layout2020 = auto()
|
||||
|
||||
@staticmethod
|
||||
def all():
|
||||
return Layout.layout2013 | Layout.layout2020
|
||||
|
||||
def to_string(self):
|
||||
if Layout.all() in self:
|
||||
return "all"
|
||||
elif Layout.layout2020 in self:
|
||||
return "2020"
|
||||
elif Layout.layout2013 in self:
|
||||
return "2013"
|
||||
else:
|
||||
return "none"
|
||||
|
||||
|
||||
class Workflow(str, Enum):
|
||||
LINUX = 'linux'
|
||||
MACOS = 'macos'
|
||||
WINDOWS = 'windows'
|
||||
ANDROID = 'android'
|
||||
|
||||
|
||||
@dataclass
|
||||
class JobConfig(object):
|
||||
name: str
|
||||
workflow: Workflow = Workflow.LINUX
|
||||
wpt_layout: Layout = Layout.none
|
||||
profile: str = "release"
|
||||
unit_tests: bool = False
|
||||
wpt_tests_to_run: str = ""
|
||||
|
||||
|
||||
def handle_preset(s: str) -> Optional[JobConfig]:
|
||||
s = s.lower()
|
||||
|
||||
if s == "linux":
|
||||
return JobConfig("Linux", Workflow.LINUX, unit_tests=True)
|
||||
elif s in ["mac", "macos"]:
|
||||
return JobConfig("MacOS", Workflow.MACOS, unit_tests=True)
|
||||
elif s in ["win", "windows"]:
|
||||
return JobConfig("Windows", Workflow.WINDOWS, unit_tests=True)
|
||||
elif s in ["wpt", "linux-wpt"]:
|
||||
return JobConfig("Linux WPT", Workflow.LINUX, unit_tests=True, wpt_layout=Layout.all())
|
||||
elif s in ["wpt-2013", "linux-wpt-2013"]:
|
||||
return JobConfig("Linux WPT legacy-layout", Workflow.LINUX, wpt_layout=Layout.layout2013)
|
||||
elif s in ["wpt-2020", "linux-wpt-2020"]:
|
||||
return JobConfig("Linux WPT layout-2020", Workflow.LINUX, wpt_layout=Layout.layout2020)
|
||||
elif s in ["mac-wpt", "wpt-mac"]:
|
||||
return JobConfig("MacOS WPT", Workflow.MACOS, wpt_layout=Layout.all())
|
||||
elif s == "mac-wpt-2013":
|
||||
return JobConfig("MacOS WPT legacy-layout", Workflow.MACOS, wpt_layout=Layout.layout2013)
|
||||
elif s == "mac-wpt-2020":
|
||||
return JobConfig("MacOS WPT layout-2020", Workflow.MACOS, wpt_layout=Layout.layout2020)
|
||||
elif s == "android":
|
||||
return JobConfig("Android", Workflow.ANDROID)
|
||||
elif s == "webgpu":
|
||||
return JobConfig("WebGPU CTS", Workflow.LINUX,
|
||||
wpt_layout=Layout.layout2020, # reftests are mode for new layout
|
||||
wpt_tests_to_run="_webgpu", # run only webgpu cts
|
||||
profile="production", # WebGPU works to slow with debug assert
|
||||
unit_tests=False) # production profile does not work with unit-tests
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class Encoder(json.JSONEncoder):
|
||||
def default(self, o):
|
||||
if isinstance(o, (Config, JobConfig)):
|
||||
return o.__dict__
|
||||
if isinstance(o, Layout):
|
||||
return o.to_string()
|
||||
return json.JSONEncoder.default(self, o)
|
||||
|
||||
|
||||
class Config(object):
|
||||
def __init__(self, s: Optional[str] = None):
|
||||
self.fail_fast: bool = False
|
||||
self.matrix: list[JobConfig] = list()
|
||||
if s is not None:
|
||||
self.parse(s)
|
||||
|
||||
def parse(self, input: str):
|
||||
input = input.lower().strip()
|
||||
|
||||
if not input:
|
||||
input = "full"
|
||||
|
||||
words: list[str] = input.split(" ")
|
||||
|
||||
for word in words:
|
||||
# Handle keywords.
|
||||
if word in ["fail-fast", "failfast", "fail_fast"]:
|
||||
self.fail_fast = True
|
||||
continue # skip over keyword
|
||||
if word == "full":
|
||||
words.extend(["linux-wpt", "macos", "windows", "android"])
|
||||
continue # skip over keyword
|
||||
|
||||
preset = handle_preset(word)
|
||||
if preset is None:
|
||||
print(f"Ignoring unknown preset {word}")
|
||||
else:
|
||||
self.matrix.append(preset)
|
||||
|
||||
def to_json(self) -> str:
|
||||
return json.dumps(self, cls=Encoder)
|
||||
|
||||
|
||||
def main():
|
||||
conf = Config(" ".join(sys.argv[1:]))
|
||||
print(conf.to_json())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
class TestParser(unittest.TestCase):
|
||||
def test_string(self):
|
||||
self.assertDictEqual(json.loads(Config("linux fail-fast").to_json()),
|
||||
{'fail_fast': True,
|
||||
'matrix': [{
|
||||
'name': 'Linux',
|
||||
'profile': 'release',
|
||||
'unit_tests': True,
|
||||
'workflow': 'linux',
|
||||
'wpt_layout': 'none',
|
||||
'wpt_tests_to_run': ''
|
||||
}]
|
||||
})
|
||||
|
||||
def test_empty(self):
|
||||
self.assertDictEqual(json.loads(Config("").to_json()),
|
||||
{"fail_fast": False, "matrix": [
|
||||
{
|
||||
"name": "Linux WPT",
|
||||
"workflow": "linux",
|
||||
"wpt_layout": "all",
|
||||
"profile": "release",
|
||||
"unit_tests": True,
|
||||
"wpt_tests_to_run": ""
|
||||
},
|
||||
{
|
||||
"name": "MacOS",
|
||||
"workflow": "macos",
|
||||
"wpt_layout": "none",
|
||||
"profile": "release",
|
||||
"unit_tests": True,
|
||||
"wpt_tests_to_run": ""
|
||||
},
|
||||
{
|
||||
"name": "Windows",
|
||||
"workflow": "windows",
|
||||
"wpt_layout": "none",
|
||||
"profile": "release",
|
||||
"unit_tests": True,
|
||||
"wpt_tests_to_run": ""
|
||||
},
|
||||
{
|
||||
"name": "Android",
|
||||
"workflow": "android",
|
||||
"wpt_layout": "none",
|
||||
"profile": "release",
|
||||
"unit_tests": False,
|
||||
"wpt_tests_to_run": ""
|
||||
}
|
||||
]})
|
||||
|
||||
def test_full(self):
|
||||
self.assertDictEqual(json.loads(Config("linux-wpt macos windows android").to_json()),
|
||||
json.loads(Config("").to_json()))
|
||||
|
||||
|
||||
def run_tests():
|
||||
verbosity = 1 if logging.getLogger().level >= logging.WARN else 2
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(TestParser)
|
||||
return unittest.TextTestRunner(verbosity=verbosity).run(suite).wasSuccessful()
|
Loading…
Add table
Add a link
Reference in a new issue