From 29c965a616bf355015833de216415625aaa34acc Mon Sep 17 00:00:00 2001 From: Finn Evers Date: Tue, 7 Apr 2026 20:57:01 +0200 Subject: [PATCH] ci: Add workflow dispatch trigger for compliance workflow (#53327) Also refines the reporting a bit. Release Notes: - N/A --- .github/workflows/compliance_check.yml | 33 ++- .github/workflows/release.yml | 63 ++++-- .../src/tasks/workflows/compliance_check.rs | 50 ++--- tooling/xtask/src/tasks/workflows/release.rs | 194 ++++++++++-------- 4 files changed, 200 insertions(+), 140 deletions(-) diff --git a/.github/workflows/compliance_check.yml b/.github/workflows/compliance_check.yml index f09c460c233b04e78df01e7828b4def737dec16e..c8bce3302ada3e56564616f145ac660fdc743d20 100644 --- a/.github/workflows/compliance_check.yml +++ b/.github/workflows/compliance_check.yml @@ -6,6 +6,7 @@ env: on: schedule: - cron: 30 17 * * 2 + workflow_dispatch: {} jobs: scheduled_compliance_check: if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') @@ -34,22 +35,44 @@ jobs: echo "tag=$TAG" >> "$GITHUB_OUTPUT" - id: run-compliance-check name: compliance_check::scheduled_compliance_check::run_compliance_check - run: cargo xtask compliance "$LATEST_TAG" --branch main --report-path target/compliance-report + run: | + echo "tag=$LATEST_TAG" >> "$GITHUB_OUTPUT" + cargo xtask compliance "$LATEST_TAG" --branch main --report-path compliance-report env: LATEST_TAG: ${{ steps.determine-version.outputs.tag }} GITHUB_APP_ID: ${{ secrets.ZED_ZIPPY_APP_ID }} GITHUB_APP_KEY: ${{ secrets.ZED_ZIPPY_APP_PRIVATE_KEY }} - - name: compliance_check::scheduled_compliance_check::send_failure_slack_notification - if: failure() + - name: '@actions/upload-artifact compliance-report.md' + if: always() + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 + with: + name: compliance-report.md + path: target/compliance-report.md + if-no-files-found: error + - name: send_compliance_slack_notification + if: always() run: | - MESSAGE="⚠️ Scheduled compliance check failed for upcoming preview release $LATEST_TAG: There are PRs with missing reviews." + REPORT_CONTENT="" + if [ -f "target/compliance-report.md" ]; then + REPORT_CONTENT=$(cat "target/compliance-report.md") + fi + + if [ "$COMPLIANCE_OUTCOME" == "success" ]; then + STATUS="✅ Scheduled compliance check passed for $COMPLIANCE_TAG" + else + STATUS="⚠️ Scheduled compliance check failed for $COMPLIANCE_TAG" + fi + + MESSAGE=$(printf "%s\n\nReport: %s\nPRs needing review: %s\n\n%s" "$STATUS" "$ARTIFACT_URL" "https://github.com/zed-industries/zed/pulls?q=is%3Apr+is%3Aclosed+label%3A%22PR+state%3Aneeds+review%22" "$REPORT_CONTENT") curl -X POST -H 'Content-type: application/json' \ --data "$(jq -n --arg text "$MESSAGE" '{"text": $text}')" \ "$SLACK_WEBHOOK" env: SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_WORKFLOW_FAILURES }} - LATEST_TAG: ${{ steps.determine-version.outputs.tag }} + COMPLIANCE_OUTCOME: ${{ steps.run-compliance-check.outcome }} + COMPLIANCE_TAG: ${{ steps.determine-version.outputs.tag }} + ARTIFACT_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}#artifacts defaults: run: shell: bash -euxo pipefail {0} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1401144ab3abda17dd4f526edd42166d37a47a49..f20f64c7a54151fedcd3b4e36b4dc492578b827d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -295,9 +295,7 @@ jobs: timeout-minutes: 60 compliance_check: if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') - runs-on: namespace-profile-16x32-ubuntu-2204 - env: - COMPLIANCE_FILE_PATH: compliance.md + runs-on: namespace-profile-2x4-ubuntu-2404 steps: - name: steps::checkout_repo uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd @@ -312,25 +310,33 @@ jobs: path: ~/.rustup - id: run-compliance-check name: release::compliance_check::run_compliance_check - run: cargo xtask compliance "$GITHUB_REF_NAME" --report-path "$COMPLIANCE_FILE_OUTPUT" + run: | + cargo xtask compliance "$GITHUB_REF_NAME" --report-path compliance-report env: GITHUB_APP_ID: ${{ secrets.ZED_ZIPPY_APP_ID }} GITHUB_APP_KEY: ${{ secrets.ZED_ZIPPY_APP_PRIVATE_KEY }} - - name: release::compliance_check::send_compliance_slack_notification + - name: '@actions/upload-artifact compliance-report.md' + if: always() + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 + with: + name: compliance-report.md + path: target/compliance-report.md + if-no-files-found: error + - name: send_compliance_slack_notification if: always() run: | - if [ "$COMPLIANCE_OUTCOME" == "success" ]; then - STATUS="✅ Compliance check passed for $GITHUB_REF_NAME" - else - STATUS="❌ Compliance check failed for $GITHUB_REF_NAME" + REPORT_CONTENT="" + if [ -f "target/compliance-report.md" ]; then + REPORT_CONTENT=$(cat "target/compliance-report.md") fi - REPORT_CONTENT="" - if [ -f "$COMPLIANCE_FILE_OUTPUT" ]; then - REPORT_CONTENT=$(cat "$REPORT_FILE") + if [ "$COMPLIANCE_OUTCOME" == "success" ]; then + STATUS="✅ Compliance check passed for $COMPLIANCE_TAG" + else + STATUS="❌ Compliance check failed for $COMPLIANCE_TAG" fi - MESSAGE=$(printf "%s\n\n%s" "$STATUS" "$REPORT_CONTENT") + MESSAGE=$(printf "%s\n\nReport: %s\nPRs needing review: %s\n\n%s" "$STATUS" "$ARTIFACT_URL" "https://github.com/zed-industries/zed/pulls?q=is%3Apr+is%3Aclosed+label%3A%22PR+state%3Aneeds+review%22" "$REPORT_CONTENT") curl -X POST -H 'Content-type: application/json' \ --data "$(jq -n --arg text "$MESSAGE" '{"text": $text}')" \ @@ -338,6 +344,9 @@ jobs: env: SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_WORKFLOW_FAILURES }} COMPLIANCE_OUTCOME: ${{ steps.run-compliance-check.outcome }} + COMPLIANCE_TAG: ${{ github.ref_name }} + ARTIFACT_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}#artifacts + timeout-minutes: 60 bundle_linux_aarch64: needs: - run_tests_linux @@ -671,32 +680,42 @@ jobs: path: ~/.rustup - id: run-post-upload-compliance-check name: release::validate_release_assets::run_post_upload_compliance_check - run: cargo xtask compliance "$GITHUB_REF_NAME" --report-path target/compliance-report + run: | + cargo xtask compliance "$GITHUB_REF_NAME" --report-path compliance-report env: GITHUB_APP_ID: ${{ secrets.ZED_ZIPPY_APP_ID }} GITHUB_APP_KEY: ${{ secrets.ZED_ZIPPY_APP_PRIVATE_KEY }} - - name: release::validate_release_assets::send_post_upload_compliance_notification + - name: '@actions/upload-artifact compliance-report.md' + if: always() + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 + with: + name: compliance-report.md + path: target/compliance-report.md + if-no-files-found: error + - name: send_compliance_slack_notification if: always() run: | - if [ -z "$COMPLIANCE_OUTCOME" ] || [ "$COMPLIANCE_OUTCOME" == "skipped" ]; then - echo "Compliance check was skipped, not sending notification" - exit 0 + REPORT_CONTENT="" + if [ -f "target/compliance-report.md" ]; then + REPORT_CONTENT=$(cat "target/compliance-report.md") fi - TAG="$GITHUB_REF_NAME" - if [ "$COMPLIANCE_OUTCOME" == "success" ]; then - MESSAGE="✅ Post-upload compliance re-check passed for $TAG" + STATUS="✅ Compliance check passed for $COMPLIANCE_TAG" else - MESSAGE="❌ Post-upload compliance re-check failed for $TAG" + STATUS="❌ Compliance check failed for $COMPLIANCE_TAG" fi + MESSAGE=$(printf "%s\n\nReport: %s\nPRs needing review: %s\n\n%s" "$STATUS" "$ARTIFACT_URL" "https://github.com/zed-industries/zed/pulls?q=is%3Apr+is%3Aclosed+label%3A%22PR+state%3Aneeds+review%22" "$REPORT_CONTENT") + curl -X POST -H 'Content-type: application/json' \ --data "$(jq -n --arg text "$MESSAGE" '{"text": $text}')" \ "$SLACK_WEBHOOK" env: SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_WORKFLOW_FAILURES }} COMPLIANCE_OUTCOME: ${{ steps.run-post-upload-compliance-check.outcome }} + COMPLIANCE_TAG: ${{ github.ref_name }} + ARTIFACT_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}#artifacts auto_release_preview: needs: - validate_release_assets diff --git a/tooling/xtask/src/tasks/workflows/compliance_check.rs b/tooling/xtask/src/tasks/workflows/compliance_check.rs index 9e2f4ae1e588c545266ec5a8246ac9781c6b668b..e61465ab8252b9538a81b875e68749cffd7c43d5 100644 --- a/tooling/xtask/src/tasks/workflows/compliance_check.rs +++ b/tooling/xtask/src/tasks/workflows/compliance_check.rs @@ -1,6 +1,8 @@ -use gh_workflow::{Event, Expression, Job, Run, Schedule, Step, Workflow}; +use gh_workflow::{Event, Job, Run, Schedule, Step, Workflow, WorkflowDispatch}; +use indoc::formatdoc; use crate::tasks::workflows::{ + release::{COMPLIANCE_REPORT_PATH, ComplianceContext, add_compliance_notification_steps}, runners, steps::{self, CommonJobConditions, named}, vars::{self, StepOutput}, @@ -10,7 +12,9 @@ pub fn compliance_check() -> Workflow { let check = scheduled_compliance_check(); named::workflow() - .on(Event::default().schedule([Schedule::new("30 17 * * 2")])) + .on(Event::default() + .schedule([Schedule::new("30 17 * * 2")]) + .workflow_dispatch(WorkflowDispatch::default())) .add_env(("CARGO_TERM_COLOR", "always")) .add_job(check.name, check.job) } @@ -32,7 +36,11 @@ fn scheduled_compliance_check() -> steps::NamedJob { fn run_compliance_check(tag: &StepOutput) -> Step { named::bash( - r#"cargo xtask compliance "$LATEST_TAG" --branch main --report-path target/compliance-report"#, + formatdoc! {r#" + echo "tag=$LATEST_TAG" >> "$GITHUB_OUTPUT" + cargo xtask compliance "$LATEST_TAG" --branch main --report-path {COMPLIANCE_REPORT_PATH} + "#, + } ) .id("run-compliance-check") .add_env(("LATEST_TAG", tag.to_string())) @@ -40,27 +48,19 @@ fn scheduled_compliance_check() -> steps::NamedJob { .add_env(("GITHUB_APP_KEY", vars::ZED_ZIPPY_APP_PRIVATE_KEY)) } - fn send_failure_slack_notification(tag: &StepOutput) -> Step { - named::bash(indoc::indoc! {r#" - MESSAGE="⚠️ Scheduled compliance check failed for upcoming preview release $LATEST_TAG: There are PRs with missing reviews." + let job = Job::default() + .with_repository_owner_guard() + .runs_on(runners::LINUX_SMALL) + .add_step(steps::checkout_repo().with_full_history()) + .add_step(steps::cache_rust_dependencies_namespace()) + .add_step(determine_version_step) + .add_step(run_compliance_check(&tag_output)); - curl -X POST -H 'Content-type: application/json' \ - --data "$(jq -n --arg text "$MESSAGE" '{"text": $text}')" \ - "$SLACK_WEBHOOK" - "#}) - .if_condition(Expression::new("failure()")) - .add_env(("SLACK_WEBHOOK", vars::SLACK_WEBHOOK_WORKFLOW_FAILURES)) - .add_env(("LATEST_TAG", tag.to_string())) - } - - named::job( - Job::default() - .with_repository_owner_guard() - .runs_on(runners::LINUX_SMALL) - .add_step(steps::checkout_repo().with_full_history()) - .add_step(steps::cache_rust_dependencies_namespace()) - .add_step(determine_version_step) - .add_step(run_compliance_check(&tag_output)) - .add_step(send_failure_slack_notification(&tag_output)), - ) + named::job(add_compliance_notification_steps( + job, + ComplianceContext::Scheduled { + tag_source: tag_output, + }, + "run-compliance-check", + )) } diff --git a/tooling/xtask/src/tasks/workflows/release.rs b/tooling/xtask/src/tasks/workflows/release.rs index 3efe3e7c5c127e8580a9ca22d2d0e1ab4e7c80e9..27f78fd74c2652f623197e3e9207dc6b91fa5c56 100644 --- a/tooling/xtask/src/tasks/workflows/release.rs +++ b/tooling/xtask/src/tasks/workflows/release.rs @@ -1,13 +1,11 @@ -use gh_workflow::{Event, Expression, Job, Push, Run, Step, Use, Workflow, ctx::Context}; +use gh_workflow::{Event, Expression, Push, Run, Step, Use, Workflow, ctx::Context}; use indoc::formatdoc; use crate::tasks::workflows::{ - run_bundling::{bundle_linux, bundle_mac, bundle_windows}, + run_bundling::{bundle_linux, bundle_mac, bundle_windows, upload_artifact}, run_tests, runners::{self, Arch, Platform}, - steps::{ - self, CommonJobConditions, FluentBuilder, NamedJob, dependant_job, named, release_job, - }, + steps::{self, FluentBuilder, NamedJob, dependant_job, named, release_job}, vars::{self, StepOutput, assets}, }; @@ -153,57 +151,100 @@ pub(crate) fn create_sentry_release() -> Step { .add_with(("environment", "production")) } -fn compliance_check() -> NamedJob { - fn run_compliance_check() -> Step { - named::bash( - r#"cargo xtask compliance "$GITHUB_REF_NAME" --report-path "$COMPLIANCE_FILE_OUTPUT""#, - ) - .id("run-compliance-check") - .add_env(("GITHUB_APP_ID", vars::ZED_ZIPPY_APP_ID)) - .add_env(("GITHUB_APP_KEY", vars::ZED_ZIPPY_APP_PRIVATE_KEY)) - } +pub(crate) const COMPLIANCE_REPORT_PATH: &str = "compliance-report"; +const COMPLIANCE_REPORT_FILE: &str = "target/compliance-report.md"; +const NEEDS_REVIEW_PULLS_URL: &str = "https://github.com/zed-industries/zed/pulls?q=is%3Apr+is%3Aclosed+label%3A%22PR+state%3Aneeds+review%22"; - fn send_compliance_slack_notification() -> Step { - named::bash(indoc::indoc! {r#" - if [ "$COMPLIANCE_OUTCOME" == "success" ]; then - STATUS="✅ Compliance check passed for $GITHUB_REF_NAME" - else - STATUS="❌ Compliance check failed for $GITHUB_REF_NAME" - fi +pub(crate) enum ComplianceContext { + Release, + Scheduled { tag_source: StepOutput }, +} - REPORT_CONTENT="" - if [ -f "$COMPLIANCE_FILE_OUTPUT" ]; then - REPORT_CONTENT=$(cat "$REPORT_FILE") - fi +pub(crate) fn add_compliance_notification_steps( + job: gh_workflow::Job, + context: ComplianceContext, + compliance_step_id: &str, +) -> gh_workflow::Job { + let upload_step = + upload_artifact(COMPLIANCE_REPORT_FILE).if_condition(Expression::new("always()")); + + let (success_prefix, failure_prefix) = match context { + ComplianceContext::Release => ("✅ Compliance check passed", "❌ Compliance check failed"), + ComplianceContext::Scheduled { .. } => ( + "✅ Scheduled compliance check passed", + "⚠️ Scheduled compliance check failed", + ), + }; - MESSAGE=$(printf "%s\n\n%s" "$STATUS" "$REPORT_CONTENT") + let script = formatdoc! {r#" + REPORT_CONTENT="" + if [ -f "{COMPLIANCE_REPORT_FILE}" ]; then + REPORT_CONTENT=$(cat "{COMPLIANCE_REPORT_FILE}") + fi - curl -X POST -H 'Content-type: application/json' \ - --data "$(jq -n --arg text "$MESSAGE" '{"text": $text}')" \ - "$SLACK_WEBHOOK" - "#}) + if [ "$COMPLIANCE_OUTCOME" == "success" ]; then + STATUS="{success_prefix} for $COMPLIANCE_TAG" + else + STATUS="{failure_prefix} for $COMPLIANCE_TAG" + fi + + MESSAGE=$(printf "%s\n\nReport: %s\nPRs needing review: %s\n\n%s" "$STATUS" "$ARTIFACT_URL" "{NEEDS_REVIEW_PULLS_URL}" "$REPORT_CONTENT") + + curl -X POST -H 'Content-type: application/json' \ + --data "$(jq -n --arg text "$MESSAGE" '{{"text": $text}}')" \ + "$SLACK_WEBHOOK" + "#, + }; + + let notification_step = Step::new("send_compliance_slack_notification") + .run(&script) .if_condition(Expression::new("always()")) .add_env(("SLACK_WEBHOOK", vars::SLACK_WEBHOOK_WORKFLOW_FAILURES)) .add_env(( "COMPLIANCE_OUTCOME", - "${{ steps.run-compliance-check.outcome }}", + format!("${{{{ steps.{compliance_step_id}.outcome }}}}"), + )) + .add_env(( + "COMPLIANCE_TAG", + match context { + ComplianceContext::Release => Context::github().ref_name().to_string(), + ComplianceContext::Scheduled { tag_source } => tag_source.to_string(), + }, )) + .add_env(( + "ARTIFACT_URL", + format!("{CURRENT_ACTION_RUN_URL}#artifacts"), + )); + + job.add_step(upload_step).add_step(notification_step) +} + +fn compliance_check() -> NamedJob { + fn run_compliance_check() -> Step { + named::bash(formatdoc! {r#" + cargo xtask compliance "$GITHUB_REF_NAME" --report-path {COMPLIANCE_REPORT_PATH} + "#, + }) + .id("run-compliance-check") + .add_env(("GITHUB_APP_ID", vars::ZED_ZIPPY_APP_ID)) + .add_env(("GITHUB_APP_KEY", vars::ZED_ZIPPY_APP_PRIVATE_KEY)) } - named::job( - Job::default() - .add_env(("COMPLIANCE_FILE_PATH", "compliance.md")) - .with_repository_owner_guard() - .runs_on(runners::LINUX_DEFAULT) - .add_step( - steps::checkout_repo() - .with_full_history() - .with_ref(Context::github().ref_()), - ) - .add_step(steps::cache_rust_dependencies_namespace()) - .add_step(run_compliance_check()) - .add_step(send_compliance_slack_notification()), - ) + let job = release_job(&[]) + .runs_on(runners::LINUX_SMALL) + .add_step( + steps::checkout_repo() + .with_full_history() + .with_ref(Context::github().ref_()), + ) + .add_step(steps::cache_rust_dependencies_namespace()) + .add_step(run_compliance_check()); + + named::job(add_compliance_notification_steps( + job, + ComplianceContext::Release, + "run-compliance-check", + )) } fn validate_release_assets(deps: &[&NamedJob]) -> NamedJob { @@ -229,54 +270,31 @@ fn validate_release_assets(deps: &[&NamedJob]) -> NamedJob { }; fn run_post_upload_compliance_check() -> Step { - named::bash( - r#"cargo xtask compliance "$GITHUB_REF_NAME" --report-path target/compliance-report"#, - ) + named::bash(formatdoc! {r#" + cargo xtask compliance "$GITHUB_REF_NAME" --report-path {COMPLIANCE_REPORT_PATH} + "#, + }) .id("run-post-upload-compliance-check") .add_env(("GITHUB_APP_ID", vars::ZED_ZIPPY_APP_ID)) .add_env(("GITHUB_APP_KEY", vars::ZED_ZIPPY_APP_PRIVATE_KEY)) } - fn send_post_upload_compliance_notification() -> Step { - named::bash(indoc::indoc! {r#" - if [ -z "$COMPLIANCE_OUTCOME" ] || [ "$COMPLIANCE_OUTCOME" == "skipped" ]; then - echo "Compliance check was skipped, not sending notification" - exit 0 - fi - - TAG="$GITHUB_REF_NAME" - - if [ "$COMPLIANCE_OUTCOME" == "success" ]; then - MESSAGE="✅ Post-upload compliance re-check passed for $TAG" - else - MESSAGE="❌ Post-upload compliance re-check failed for $TAG" - fi - - curl -X POST -H 'Content-type: application/json' \ - --data "$(jq -n --arg text "$MESSAGE" '{"text": $text}')" \ - "$SLACK_WEBHOOK" - "#}) - .if_condition(Expression::new("always()")) - .add_env(("SLACK_WEBHOOK", vars::SLACK_WEBHOOK_WORKFLOW_FAILURES)) - .add_env(( - "COMPLIANCE_OUTCOME", - "${{ steps.run-post-upload-compliance-check.outcome }}", - )) - } - - named::job( - dependant_job(deps) - .runs_on(runners::LINUX_SMALL) - .add_step(named::bash(&validation_script).add_env(("GITHUB_TOKEN", vars::GITHUB_TOKEN))) - .add_step( - steps::checkout_repo() - .with_full_history() - .with_ref(Context::github().ref_()), - ) - .add_step(steps::cache_rust_dependencies_namespace()) - .add_step(run_post_upload_compliance_check()) - .add_step(send_post_upload_compliance_notification()), - ) + let job = dependant_job(deps) + .runs_on(runners::LINUX_SMALL) + .add_step(named::bash(&validation_script).add_env(("GITHUB_TOKEN", vars::GITHUB_TOKEN))) + .add_step( + steps::checkout_repo() + .with_full_history() + .with_ref("${{ github.ref }}"), + ) + .add_step(steps::cache_rust_dependencies_namespace()) + .add_step(run_post_upload_compliance_check()); + + named::job(add_compliance_notification_steps( + job, + ComplianceContext::Release, + "run-post-upload-compliance-check", + )) } fn auto_release_preview(deps: &[&NamedJob]) -> NamedJob {