mirror of
https://github.com/zed-industries/zed.git
synced 2026-07-09 16:00:35 +00:00
ci: Set and enforce more default permissions (#60222)
Release Notes: - N/A
This commit is contained in:
parent
5d6c88cdb7
commit
283d5054ec
62 changed files with 388 additions and 136 deletions
2
.github/workflows/after_release.yml
vendored
2
.github/workflows/after_release.yml
vendored
|
|
@ -22,6 +22,8 @@ on:
|
|||
description: body
|
||||
type: string
|
||||
default: ''
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
rebuild_releases_page:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
|
|
|
|||
5
.github/workflows/autofix_pr.yml
vendored
5
.github/workflows/autofix_pr.yml
vendored
|
|
@ -13,9 +13,14 @@ on:
|
|||
description: run_clippy
|
||||
type: boolean
|
||||
default: 'true'
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
run_autofix:
|
||||
runs-on: namespace-profile-16x32-ubuntu-2204
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
env:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
|
|
|
|||
5
.github/workflows/bump_collab_staging.yml
vendored
5
.github/workflows/bump_collab_staging.yml
vendored
|
|
@ -5,10 +5,15 @@ on:
|
|||
# Fire every day at 16:00 UTC (At the start of the US workday)
|
||||
- cron: "0 16 * * *"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
update-collab-staging-tag:
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: namespace-profile-2x4-ubuntu-2404
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
|
|
|
|||
4
.github/workflows/bump_patch_version.yml
vendored
4
.github/workflows/bump_patch_version.yml
vendored
|
|
@ -8,10 +8,14 @@ on:
|
|||
description: Branch name to run on
|
||||
required: true
|
||||
type: string
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
run_bump_patch_version:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
runs-on: namespace-profile-16x32-ubuntu-2204
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- id: generate-token
|
||||
name: steps::authenticate_as_zippy
|
||||
|
|
|
|||
9
.github/workflows/bump_zed_version.yml
vendored
9
.github/workflows/bump_zed_version.yml
vendored
|
|
@ -8,6 +8,8 @@ on:
|
|||
description: 'Which channels to bump: all, main, preview, or stable'
|
||||
type: string
|
||||
default: all
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
resolve_versions:
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
|
|
@ -86,6 +88,9 @@ jobs:
|
|||
- resolve_versions
|
||||
if: inputs.target == 'all' || inputs.target == 'main'
|
||||
runs-on: namespace-profile-16x32-ubuntu-2204
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- id: generate-token
|
||||
name: steps::authenticate_as_zippy
|
||||
|
|
@ -127,6 +132,8 @@ jobs:
|
|||
- resolve_versions
|
||||
if: inputs.target == 'all' || inputs.target == 'preview'
|
||||
runs-on: namespace-profile-16x32-ubuntu-2204
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- id: generate-token
|
||||
name: steps::authenticate_as_zippy
|
||||
|
|
@ -180,6 +187,8 @@ jobs:
|
|||
- resolve_versions
|
||||
if: inputs.target == 'all' || inputs.target == 'stable'
|
||||
runs-on: namespace-profile-16x32-ubuntu-2204
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- id: generate-token
|
||||
name: steps::authenticate_as_zippy
|
||||
|
|
|
|||
2
.github/workflows/cherry_pick.yml
vendored
2
.github/workflows/cherry_pick.yml
vendored
|
|
@ -21,6 +21,8 @@ on:
|
|||
description: pr_number
|
||||
required: true
|
||||
type: string
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
run_cherry_pick:
|
||||
runs-on: namespace-profile-2x4-ubuntu-2404
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ concurrency:
|
|||
group: potential-duplicate-check-${{ github.event.issue.number || inputs.issue_number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
identify-duplicates:
|
||||
# For manual testing, allow running on any branch; for automatic runs, only on main repo
|
||||
|
|
|
|||
|
|
@ -13,10 +13,15 @@ on:
|
|||
type: number
|
||||
default: 1000
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: namespace-profile-2x4-ubuntu-2404
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10
|
||||
with:
|
||||
|
|
|
|||
|
|
@ -5,10 +5,16 @@ on:
|
|||
- cron: "0 */12 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
update_top_ranking_issues:
|
||||
runs-on: namespace-profile-2x4-ubuntu-2404
|
||||
if: github.repository == 'zed-industries/zed'
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
steps:
|
||||
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
- name: Set up uv
|
||||
|
|
|
|||
|
|
@ -5,10 +5,16 @@ on:
|
|||
- cron: "0 15 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
update_top_ranking_issues:
|
||||
runs-on: namespace-profile-2x4-ubuntu-2404
|
||||
if: github.repository == 'zed-industries/zed'
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
steps:
|
||||
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
- name: Set up uv
|
||||
|
|
|
|||
2
.github/workflows/compliance_check.yml
vendored
2
.github/workflows/compliance_check.yml
vendored
|
|
@ -7,6 +7,8 @@ on:
|
|||
schedule:
|
||||
- cron: 30 17 * * 2
|
||||
workflow_dispatch: {}
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
scheduled_compliance_check:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
|
|
|
|||
3
.github/workflows/congrats.yml
vendored
3
.github/workflows/congrats.yml
vendored
|
|
@ -4,6 +4,9 @@ on:
|
|||
push:
|
||||
branches: [main]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
check-author:
|
||||
if: ${{ github.repository_owner == 'zed-industries' }}
|
||||
|
|
|
|||
2
.github/workflows/danger.yml
vendored
2
.github/workflows/danger.yml
vendored
|
|
@ -11,6 +11,8 @@ on:
|
|||
- edited
|
||||
branches:
|
||||
- main
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
danger:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
|
|
|
|||
2
.github/workflows/deploy_collab.yml
vendored
2
.github/workflows/deploy_collab.yml
vendored
|
|
@ -7,6 +7,8 @@ on:
|
|||
push:
|
||||
tags:
|
||||
- collab-production
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
style:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
|
|
|
|||
2
.github/workflows/deploy_docs.yml
vendored
2
.github/workflows/deploy_docs.yml
vendored
|
|
@ -35,6 +35,8 @@ on:
|
|||
description: Git ref to checkout and deploy. Defaults to event SHA when omitted.
|
||||
type: string
|
||||
default: ''
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
deploy_docs:
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
|
|
|
|||
1
.github/workflows/deploy_nightly_docs.yml
vendored
1
.github/workflows/deploy_nightly_docs.yml
vendored
|
|
@ -5,6 +5,7 @@ on:
|
|||
push:
|
||||
branches:
|
||||
- main
|
||||
permissions: {}
|
||||
jobs:
|
||||
deploy_docs:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
|
|
|
|||
4
.github/workflows/docs_suggestions.yml
vendored
4
.github/workflows/docs_suggestions.yml
vendored
|
|
@ -42,6 +42,10 @@ on:
|
|||
- immediate
|
||||
default: batch
|
||||
|
||||
# Both jobs below declare their own `permissions:` blocks, so no job uses this
|
||||
# top-level default. Least privilege therefore grants nothing here.
|
||||
permissions: {}
|
||||
|
||||
env:
|
||||
DROID_MODEL: claude-sonnet-4-5-20250929
|
||||
SUGGESTIONS_BRANCH: docs/suggestions-pending
|
||||
|
|
|
|||
2
.github/workflows/extension_auto_bump.yml
vendored
2
.github/workflows/extension_auto_bump.yml
vendored
|
|
@ -10,6 +10,8 @@ on:
|
|||
- '!extensions/test-extension/**'
|
||||
- '!extensions/workflows/**'
|
||||
- '!extensions/*.md'
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
detect_changed_extensions:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
|
|
|
|||
2
.github/workflows/extension_bump.yml
vendored
2
.github/workflows/extension_bump.yml
vendored
|
|
@ -28,6 +28,8 @@ on:
|
|||
app-secret:
|
||||
description: The app secret for the corresponding app ID
|
||||
required: true
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
check_version_changed:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
|
|
|
|||
2
.github/workflows/extension_tests.yml
vendored
2
.github/workflows/extension_tests.yml
vendored
|
|
@ -15,6 +15,8 @@ on:
|
|||
description: working-directory
|
||||
type: string
|
||||
default: .
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
orchestrate:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ on:
|
|||
description: Description for the changes to be expected with this rollout
|
||||
type: string
|
||||
default: ''
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
fetch_extension_repos:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') && (github.ref == 'refs/tags/extension-workflows' || github.ref == 'refs/heads/main')
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ on:
|
|||
issues:
|
||||
types: [labeled]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
handle-good-first-issue:
|
||||
if: github.event.label.name == '.contrib/good first issue' && github.repository_owner == 'zed-industries'
|
||||
|
|
|
|||
3
.github/workflows/hotfix-review-monitor.yml
vendored
3
.github/workflows/hotfix-review-monitor.yml
vendored
|
|
@ -21,12 +21,13 @@ on:
|
|||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
check-hotfix-reviews:
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: read
|
||||
timeout-minutes: 5
|
||||
env:
|
||||
REPO: ${{ github.repository }}
|
||||
|
|
|
|||
2
.github/workflows/nix_build.yml
vendored
2
.github/workflows/nix_build.yml
vendored
|
|
@ -9,6 +9,8 @@ on:
|
|||
types:
|
||||
- labeled
|
||||
- synchronize
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
build_nix_linux_x86_64:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') && ((github.event.action == 'labeled' && (github.event.label.name == 'run-nix' || github.event.label.name == 'run-bundling')) || (github.event.action == 'synchronize' && (contains(github.event.pull_request.labels.*.name, 'run-nix') || contains(github.event.pull_request.labels.*.name, 'run-bundling'))))
|
||||
|
|
|
|||
2
.github/workflows/publish_extension_cli.yml
vendored
2
.github/workflows/publish_extension_cli.yml
vendored
|
|
@ -11,6 +11,8 @@ on:
|
|||
description: Describe why the extension CLI is being bumped and/or what changes are included.
|
||||
required: true
|
||||
type: string
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
publish_job:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') && github.ref == 'refs/heads/main'
|
||||
|
|
|
|||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
|
|
@ -8,6 +8,8 @@ on:
|
|||
push:
|
||||
tags:
|
||||
- v*
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
run_tests_mac:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
|
|
@ -723,6 +725,8 @@ jobs:
|
|||
- bundle_windows_aarch64
|
||||
- bundle_windows_x86_64
|
||||
runs-on: namespace-profile-4x8-ubuntu-2204
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: release::download_workflow_artifacts
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
|
||||
|
|
|
|||
2
.github/workflows/release_nightly.yml
vendored
2
.github/workflows/release_nightly.yml
vendored
|
|
@ -8,6 +8,8 @@ on:
|
|||
schedule:
|
||||
- cron: 0 */4 * * *
|
||||
workflow_dispatch: {}
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
check_nightly_tag:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
|
|
|
|||
2
.github/workflows/run_bundling.yml
vendored
2
.github/workflows/run_bundling.yml
vendored
|
|
@ -9,6 +9,8 @@ on:
|
|||
types:
|
||||
- labeled
|
||||
- synchronize
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
bundle_linux_aarch64:
|
||||
if: |-
|
||||
|
|
|
|||
2
.github/workflows/run_tests.yml
vendored
2
.github/workflows/run_tests.yml
vendored
|
|
@ -14,6 +14,8 @@ on:
|
|||
branches:
|
||||
- main
|
||||
- v[0-9]+.[0-9]+.x
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
orchestrate:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ on:
|
|||
- "Guild New PR Notification"
|
||||
types: [completed]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
notify-slack:
|
||||
if: >-
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ on:
|
|||
issues:
|
||||
types: [labeled]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
PRIORITY_LABELS: '["priority:P0", "priority:P1"]'
|
||||
REPRODUCIBLE_LABEL: 'state:reproducible'
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ on:
|
|||
label:
|
||||
types: [created]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
notify-slack:
|
||||
if: >-
|
||||
|
|
|
|||
3
.github/workflows/stale-pr-reminder.yml
vendored
3
.github/workflows/stale-pr-reminder.yml
vendored
|
|
@ -20,13 +20,14 @@ on:
|
|||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
check-stale-prs:
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
permissions:
|
||||
pull-requests: read
|
||||
env:
|
||||
REPO: ${{ github.repository }}
|
||||
# Only surface PRs created on or after this date. Update this if the
|
||||
|
|
|
|||
|
|
@ -5,10 +5,16 @@ on:
|
|||
- cron: "0 6 * * 1,4" # Mondays and Thursdays at 6 AM UTC
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
update-duplicate-magnets:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'zed-industries/zed'
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
steps:
|
||||
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ on:
|
|||
push:
|
||||
branches:
|
||||
- main
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
call_extension_tests:
|
||||
permissions:
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ on:
|
|||
paths-ignore:
|
||||
- .github/**
|
||||
workflow_dispatch: {}
|
||||
permissions: {}
|
||||
jobs:
|
||||
determine_bump_type:
|
||||
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
|
||||
|
|
|
|||
|
|
@ -1,20 +1,22 @@
|
|||
mod check_permissions;
|
||||
mod check_run_patterns;
|
||||
|
||||
use std::{fs, path::PathBuf};
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use annotate_snippets::Renderer;
|
||||
use annotate_snippets::{Group, Renderer};
|
||||
use anyhow::{Result, anyhow};
|
||||
use clap::Parser;
|
||||
use itertools::{Either, Itertools};
|
||||
use serde_yaml::Value;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::tasks::{
|
||||
workflow_checks::check_run_patterns::{
|
||||
RunValidationError, WorkflowFile, WorkflowValidationError,
|
||||
},
|
||||
workflows::WorkflowType,
|
||||
};
|
||||
use crate::tasks::workflows::WorkflowType;
|
||||
|
||||
use check_permissions::PermissionsError;
|
||||
use check_run_patterns::RunValidationError;
|
||||
|
||||
pub use check_run_patterns::validate_run_command;
|
||||
|
||||
|
|
@ -36,14 +38,14 @@ pub fn validate(_: WorkflowValidationArgs) -> Result<()> {
|
|||
parsing_errors.into_iter().join("\n")
|
||||
))
|
||||
} else if !file_errors.is_empty() {
|
||||
let errors: Vec<_> = file_errors
|
||||
let groups: Vec<_> = file_errors
|
||||
.iter()
|
||||
.map(|error| error.annotation_group())
|
||||
.flat_map(|error| error.annotation_groups())
|
||||
.collect();
|
||||
|
||||
let renderer =
|
||||
Renderer::styled().decor_style(annotate_snippets::renderer::DecorStyle::Ascii);
|
||||
println!("{}", renderer.render(errors.as_slice()));
|
||||
println!("{}", renderer.render(groups.as_slice()));
|
||||
|
||||
Err(anyhow!("Workflow checks failed!"))
|
||||
} else {
|
||||
|
|
@ -51,6 +53,61 @@ pub fn validate(_: WorkflowValidationArgs) -> Result<()> {
|
|||
}
|
||||
}
|
||||
|
||||
struct WorkflowFile {
|
||||
raw_content: String,
|
||||
parsed_content: Value,
|
||||
}
|
||||
|
||||
impl WorkflowFile {
|
||||
fn load(workflow_file_path: &Path) -> Result<Self> {
|
||||
fs::read_to_string(workflow_file_path)
|
||||
.map_err(|_| {
|
||||
anyhow!(
|
||||
"Could not read workflow file at {}",
|
||||
workflow_file_path.display()
|
||||
)
|
||||
})
|
||||
.and_then(|file_content| {
|
||||
serde_yaml::from_str(&file_content)
|
||||
.map(|parsed_content| Self {
|
||||
raw_content: file_content,
|
||||
parsed_content,
|
||||
})
|
||||
.map_err(|e| anyhow!("Failed to parse workflow file: {e:?}"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A single kind of validation failure found within a workflow file.
|
||||
enum ValidationError {
|
||||
RunInjection(RunValidationError),
|
||||
Permissions(PermissionsError),
|
||||
}
|
||||
|
||||
impl ValidationError {
|
||||
fn annotation_group<'a>(&self, file_path: &Path, raw_content: &'a str) -> Group<'a> {
|
||||
match self {
|
||||
ValidationError::RunInjection(error) => error.annotation_group(file_path, raw_content),
|
||||
ValidationError::Permissions(error) => error.annotation_group(file_path, raw_content),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct WorkflowValidationError {
|
||||
file_path: PathBuf,
|
||||
contents: WorkflowFile,
|
||||
errors: Vec<ValidationError>,
|
||||
}
|
||||
|
||||
impl WorkflowValidationError {
|
||||
fn annotation_groups(&self) -> Vec<Group<'_>> {
|
||||
self.errors
|
||||
.iter()
|
||||
.map(|error| error.annotation_group(&self.file_path, &self.contents.raw_content))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
enum WorkflowError {
|
||||
ParseError(anyhow::Error),
|
||||
ValidationError(Box<WorkflowValidationError>),
|
||||
|
|
@ -73,41 +130,47 @@ fn get_all_workflow_files() -> impl Iterator<Item = PathBuf> {
|
|||
}
|
||||
|
||||
fn check_workflow(workflow_file_path: PathBuf) -> Result<(), WorkflowError> {
|
||||
fn collect_errors(
|
||||
iter: impl Iterator<Item = Result<(), Vec<RunValidationError>>>,
|
||||
) -> Result<(), Vec<RunValidationError>> {
|
||||
Some(iter.flat_map(Result::err).flatten().collect::<Vec<_>>())
|
||||
.filter(|errors| !errors.is_empty())
|
||||
.map_or(Ok(()), Err)
|
||||
}
|
||||
|
||||
fn check_recursive(key: &Value, value: &Value) -> Result<(), Vec<RunValidationError>> {
|
||||
match value {
|
||||
Value::Mapping(mapping) => collect_errors(
|
||||
mapping
|
||||
.into_iter()
|
||||
.map(|(key, value)| check_recursive(key, value)),
|
||||
),
|
||||
Value::Sequence(sequence) => collect_errors(
|
||||
sequence
|
||||
.into_iter()
|
||||
.map(|value| check_recursive(key, value)),
|
||||
),
|
||||
Value::String(string) => check_string(key, string).map_err(|error| vec![error]),
|
||||
Value::Null | Value::Bool(_) | Value::Number(_) | Value::Tagged(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
let file_content =
|
||||
WorkflowFile::load(&workflow_file_path).map_err(WorkflowError::ParseError)?;
|
||||
|
||||
check_recursive(&Value::Null, &file_content.parsed_content).map_err(|errors| {
|
||||
WorkflowError::ValidationError(Box::new(WorkflowValidationError::new(
|
||||
errors,
|
||||
file_content,
|
||||
workflow_file_path,
|
||||
let mut errors = Vec::new();
|
||||
|
||||
if let Err(error) = check_permissions::validate_permissions(&file_content.parsed_content) {
|
||||
errors.push(ValidationError::Permissions(error));
|
||||
}
|
||||
|
||||
errors.extend(
|
||||
collect_run_injection_errors(&Value::Null, &file_content.parsed_content)
|
||||
.into_iter()
|
||||
.map(ValidationError::RunInjection),
|
||||
);
|
||||
|
||||
if errors.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(WorkflowError::ValidationError(Box::new(
|
||||
WorkflowValidationError {
|
||||
file_path: workflow_file_path,
|
||||
contents: file_content,
|
||||
errors,
|
||||
},
|
||||
)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_run_injection_errors(key: &Value, value: &Value) -> Vec<RunValidationError> {
|
||||
match value {
|
||||
Value::Mapping(mapping) => mapping
|
||||
.iter()
|
||||
.flat_map(|(key, value)| collect_run_injection_errors(key, value))
|
||||
.collect(),
|
||||
Value::Sequence(sequence) => sequence
|
||||
.iter()
|
||||
.flat_map(|value| collect_run_injection_errors(key, value))
|
||||
.collect(),
|
||||
Value::String(string) => check_string(key, string).err().into_iter().collect(),
|
||||
Value::Null | Value::Bool(_) | Value::Number(_) | Value::Tagged(_) => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_string(key: &Value, value: &str) -> Result<(), RunValidationError> {
|
||||
|
|
|
|||
74
tooling/xtask/src/tasks/workflow_checks/check_permissions.rs
Normal file
74
tooling/xtask/src/tasks/workflow_checks/check_permissions.rs
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
use annotate_snippets::{AnnotationKind, Group, Level, Snippet};
|
||||
use serde_yaml::Value;
|
||||
use std::{ops::Range, path::Path};
|
||||
|
||||
pub enum PermissionsError {
|
||||
/// No top-level `permissions:` key.
|
||||
Missing,
|
||||
/// Top-level default grants more than `contents: read`.
|
||||
ExcessiveDefault,
|
||||
}
|
||||
|
||||
/// Validates a workflow's top-level (default) `permissions:`.
|
||||
///
|
||||
/// Every workflow must declare one, and it may grant at most `contents: read`;
|
||||
/// jobs that need more must request it at the job level.
|
||||
pub fn validate_permissions(workflow: &Value) -> Result<(), PermissionsError> {
|
||||
let Some(permissions) = workflow.get("permissions") else {
|
||||
return Err(PermissionsError::Missing);
|
||||
};
|
||||
|
||||
let is_minimal = match permissions {
|
||||
Value::Mapping(mapping) => mapping.iter().all(|(scope, level)| {
|
||||
let level = level.as_str();
|
||||
level == Some("none") || (scope.as_str() == Some("contents") && level == Some("read"))
|
||||
}),
|
||||
// String forms such as `read-all`/`write-all` always exceed the allowance.
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if is_minimal {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(PermissionsError::ExcessiveDefault)
|
||||
}
|
||||
}
|
||||
|
||||
impl PermissionsError {
|
||||
pub fn annotation_group<'a>(&self, file_path: &Path, raw_content: &'a str) -> Group<'a> {
|
||||
let (title, span, label) = match self {
|
||||
PermissionsError::Missing => (
|
||||
"Workflow is missing a top-level `permissions:` key",
|
||||
first_line_span(raw_content, "name:"),
|
||||
"Add a top-level `permissions:` key so this workflow defaults to the least privilege it needs",
|
||||
),
|
||||
PermissionsError::ExcessiveDefault => (
|
||||
"Top-level workflow permissions must grant at most `contents: read`",
|
||||
first_line_span(raw_content, "permissions:"),
|
||||
"Lower the default to `contents: read` (or `{}`) and move elevated permissions to the jobs that need them",
|
||||
),
|
||||
};
|
||||
|
||||
Level::ERROR.primary_title(title).element(
|
||||
Snippet::source(raw_content)
|
||||
.path(file_path.display().to_string())
|
||||
.annotations(std::iter::once(
|
||||
AnnotationKind::Primary.span(span).label(label),
|
||||
)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Span of the first column-0 line starting with `prefix`, else empty.
|
||||
fn first_line_span(raw_content: &str, prefix: &str) -> Range<usize> {
|
||||
let mut offset = 0;
|
||||
for line in raw_content.lines() {
|
||||
let start = offset;
|
||||
offset += line.len() + 1;
|
||||
if line.starts_with(prefix) {
|
||||
return start..start + line.len();
|
||||
}
|
||||
}
|
||||
|
||||
Default::default()
|
||||
}
|
||||
|
|
@ -1,72 +1,25 @@
|
|||
use annotate_snippets::{AnnotationKind, Group, Level, Snippet};
|
||||
use anyhow::{Result, anyhow};
|
||||
use regex::Regex;
|
||||
use serde_yaml::Value;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs,
|
||||
ops::Range,
|
||||
path::{Path, PathBuf},
|
||||
sync::LazyLock,
|
||||
};
|
||||
use std::{collections::HashMap, ops::Range, path::Path, sync::LazyLock};
|
||||
|
||||
static GITHUB_INPUT_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
|
||||
Regex::new(r#"\$\{\{[[:blank:]]*([[:alnum:]]|[[:punct:]])+?[[:blank:]]*\}\}"#)
|
||||
.expect("Should compile")
|
||||
});
|
||||
|
||||
pub struct WorkflowFile {
|
||||
raw_content: String,
|
||||
pub parsed_content: Value,
|
||||
pub struct RunValidationError {
|
||||
found_injection_patterns: Vec<(String, Range<usize>)>,
|
||||
}
|
||||
|
||||
impl WorkflowFile {
|
||||
pub fn load(workflow_file_path: &Path) -> Result<Self> {
|
||||
fs::read_to_string(workflow_file_path)
|
||||
.map_err(|_| {
|
||||
anyhow!(
|
||||
"Could not read workflow file at {}",
|
||||
workflow_file_path.display()
|
||||
)
|
||||
})
|
||||
.and_then(|file_content| {
|
||||
serde_yaml::from_str(&file_content)
|
||||
.map(|parsed_content| Self {
|
||||
raw_content: file_content,
|
||||
parsed_content,
|
||||
})
|
||||
.map_err(|e| anyhow!("Failed to parse workflow file: {e:?}"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WorkflowValidationError {
|
||||
file_path: PathBuf,
|
||||
contents: WorkflowFile,
|
||||
errors: Vec<RunValidationError>,
|
||||
}
|
||||
|
||||
impl WorkflowValidationError {
|
||||
pub fn new(
|
||||
errors: Vec<RunValidationError>,
|
||||
contents: WorkflowFile,
|
||||
file_path: PathBuf,
|
||||
) -> Self {
|
||||
Self {
|
||||
file_path,
|
||||
contents,
|
||||
errors,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn annotation_group<'a>(&'a self) -> Group<'a> {
|
||||
let raw_content = &self.contents.raw_content;
|
||||
impl RunValidationError {
|
||||
/// Renders the GitHub input injection patterns found in a single `run:`
|
||||
/// command as one diagnostic group.
|
||||
pub fn annotation_group<'a>(&self, file_path: &Path, raw_content: &'a str) -> Group<'a> {
|
||||
let mut identical_lines = HashMap::new();
|
||||
|
||||
let ranges = self
|
||||
.errors
|
||||
.found_injection_patterns
|
||||
.iter()
|
||||
.flat_map(|error| error.found_injection_patterns.iter())
|
||||
.map(|(line, pattern_range)| {
|
||||
let initial_offset = identical_lines
|
||||
.get(&(line.as_str(), pattern_range.start))
|
||||
|
|
@ -89,8 +42,8 @@ impl WorkflowValidationError {
|
|||
Level::ERROR
|
||||
.primary_title("Found GitHub input injection in run command")
|
||||
.element(
|
||||
Snippet::source(&self.contents.raw_content)
|
||||
.path(self.file_path.display().to_string())
|
||||
Snippet::source(raw_content)
|
||||
.path(file_path.display().to_string())
|
||||
.annotations(ranges.map(|range| {
|
||||
AnnotationKind::Primary
|
||||
.span(range)
|
||||
|
|
@ -100,10 +53,6 @@ impl WorkflowValidationError {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct RunValidationError {
|
||||
found_injection_patterns: Vec<(String, Range<usize>)>,
|
||||
}
|
||||
|
||||
pub fn validate_run_command(command: &str) -> Result<(), RunValidationError> {
|
||||
let patterns: Vec<_> = command
|
||||
.lines()
|
||||
|
|
|
|||
|
|
@ -136,8 +136,8 @@ impl WorkflowFile {
|
|||
.as_ref()
|
||||
.expect("Workflow must have a name at this point");
|
||||
let filename = format!(
|
||||
"{}.yml",
|
||||
workflow_name.rsplit("::").next().unwrap_or(workflow_name)
|
||||
"{workflow_name}.yml",
|
||||
workflow_name = workflow_name.rsplit("::").next().unwrap_or(workflow_name)
|
||||
);
|
||||
|
||||
let workflow_path = workflow_folder.join(filename);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ use crate::tasks::workflows::{
|
|||
deploy_docs::deploy_docs_workflow_call,
|
||||
release::{self, notify_on_failure},
|
||||
runners,
|
||||
steps::{CommonJobConditions, NamedJob, checkout_repo, dependant_job, named},
|
||||
steps::{
|
||||
CommonJobConditions, CommonPermissionSets, NamedJob, checkout_repo, dependant_job, named,
|
||||
},
|
||||
vars::{self, StepOutput, WorkflowInput},
|
||||
};
|
||||
|
||||
|
|
@ -39,6 +41,7 @@ pub fn after_release() -> Workflow {
|
|||
};
|
||||
|
||||
named::workflow()
|
||||
.with_minimal_permissions()
|
||||
.add_env(("TAG_NAME", TAG_NAME_ENV))
|
||||
.add_env(("IS_PRERELEASE", IS_PRERELEASE_ENV))
|
||||
.on(Event::default()
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ use gh_workflow::*;
|
|||
use crate::tasks::workflows::{
|
||||
runners,
|
||||
steps::{
|
||||
self, DownloadArtifactStep, FluentBuilder, IfNoFilesFound, NamedJob, RepositoryTarget,
|
||||
TokenPermissions, UploadArtifactStep, ZippyGitIdentity, named, use_clang,
|
||||
self, CommonPermissionSets, DownloadArtifactStep, FluentBuilder, IfNoFilesFound, NamedJob,
|
||||
RepositoryTarget, TokenPermissions, UploadArtifactStep, ZippyGitIdentity, named, use_clang,
|
||||
},
|
||||
vars::{self, StepOutput, WorkflowInput},
|
||||
};
|
||||
|
|
@ -15,6 +15,7 @@ pub fn autofix_pr() -> Workflow {
|
|||
let run_autofix = run_autofix(&pr_number, &run_clippy);
|
||||
let commit_changes = commit_changes(&pr_number, &run_autofix);
|
||||
named::workflow()
|
||||
.with_minimal_permissions()
|
||||
.run_name(format!("autofix PR #{pr_number}"))
|
||||
.on(Event::default().workflow_dispatch(
|
||||
WorkflowDispatch::default()
|
||||
|
|
@ -91,6 +92,11 @@ fn run_autofix(pr_number: &WorkflowInput, run_clippy: &WorkflowInput) -> NamedJo
|
|||
named::job(use_clang(
|
||||
Job::default()
|
||||
.runs_on(runners::LINUX_DEFAULT)
|
||||
.permissions(
|
||||
Permissions::default()
|
||||
.contents(Level::Read)
|
||||
.pull_requests(Level::Read),
|
||||
)
|
||||
.outputs([(
|
||||
"has_changes".to_owned(),
|
||||
"${{ steps.create-patch.outputs.has_changes }}".to_owned(),
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use gh_workflow::*;
|
|||
|
||||
use crate::tasks::workflows::{
|
||||
runners,
|
||||
steps::{self, CheckoutStep, CommonJobConditions, named},
|
||||
steps::{self, CheckoutStep, CommonJobConditions, CommonPermissionSets, named},
|
||||
vars::{StepOutput, WorkflowInput},
|
||||
};
|
||||
|
||||
|
|
@ -10,6 +10,7 @@ pub fn bump_patch_version() -> Workflow {
|
|||
let branch = WorkflowInput::string("branch", None).description("Branch name to run on");
|
||||
let bump_patch_version_job = run_bump_patch_version(&branch);
|
||||
named::workflow()
|
||||
.with_minimal_permissions()
|
||||
.on(Event::default()
|
||||
.workflow_dispatch(WorkflowDispatch::default().add_input(branch.name, branch.input())))
|
||||
.concurrency(
|
||||
|
|
@ -80,6 +81,7 @@ fn run_bump_patch_version(branch: &WorkflowInput) -> steps::NamedJob {
|
|||
named::job(
|
||||
Job::default()
|
||||
.with_repository_owner_guard()
|
||||
.permissions(Permissions::default().contents(Level::Write))
|
||||
.runs_on(runners::LINUX_DEFAULT)
|
||||
.add_step(authenticate)
|
||||
.add_step(checkout_branch(branch, &token))
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use gh_workflow::*;
|
|||
|
||||
use crate::tasks::workflows::{
|
||||
runners,
|
||||
steps::{self, named},
|
||||
steps::{self, CommonPermissionSets, named},
|
||||
vars::{self, StepOutput, WorkflowInput},
|
||||
};
|
||||
|
||||
|
|
@ -17,6 +17,7 @@ pub fn bump_zed_version() -> Workflow {
|
|||
let stable_job = promote_to_stable(&target, &versions_job, &outputs);
|
||||
|
||||
named::workflow()
|
||||
.with_minimal_permissions()
|
||||
.on(Event::default()
|
||||
.workflow_dispatch(WorkflowDispatch::default().add_input(target.name, target.input())))
|
||||
.add_job(versions_job.name, versions_job.job)
|
||||
|
|
@ -142,6 +143,11 @@ fn bump_main(
|
|||
target.expr(),
|
||||
target.expr(),
|
||||
)))
|
||||
.permissions(
|
||||
Permissions::default()
|
||||
.contents(Level::Write)
|
||||
.pull_requests(Level::Write),
|
||||
)
|
||||
.needs(vec![versions_job.name.clone()])
|
||||
.runs_on(runners::LINUX_DEFAULT)
|
||||
.add_step(authenticate)
|
||||
|
|
@ -193,6 +199,7 @@ fn create_preview_branch(
|
|||
target.expr(),
|
||||
target.expr(),
|
||||
)))
|
||||
.permissions(Permissions::default().contents(Level::Write))
|
||||
.needs(vec![versions_job.name.clone()])
|
||||
.runs_on(runners::LINUX_DEFAULT)
|
||||
.add_step(authenticate)
|
||||
|
|
@ -250,6 +257,7 @@ fn promote_to_stable(
|
|||
target.expr(),
|
||||
target.expr(),
|
||||
)))
|
||||
.permissions(Permissions::default().contents(Level::Write))
|
||||
.needs(vec![versions_job.name.clone()])
|
||||
.runs_on(runners::LINUX_DEFAULT)
|
||||
.add_step(authenticate)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ use gh_workflow::*;
|
|||
|
||||
use crate::tasks::workflows::{
|
||||
runners,
|
||||
steps::{self, NamedJob, RepositoryTarget, TokenPermissions, ZippyGitIdentity, named},
|
||||
steps::{
|
||||
self, CommonPermissionSets, NamedJob, RepositoryTarget, TokenPermissions, ZippyGitIdentity,
|
||||
named,
|
||||
},
|
||||
vars::{StepOutput, WorkflowInput},
|
||||
};
|
||||
|
||||
|
|
@ -13,6 +16,7 @@ pub fn cherry_pick() -> Workflow {
|
|||
let pr_number = WorkflowInput::string("pr_number", None);
|
||||
let cherry_pick = run_cherry_pick(&branch, &commit, &channel);
|
||||
named::workflow()
|
||||
.with_minimal_permissions()
|
||||
.run_name(format!("cherry_pick to {channel} #{pr_number}"))
|
||||
.on(Event::default().workflow_dispatch(
|
||||
WorkflowDispatch::default()
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use gh_workflow::{Event, Job, Schedule, Workflow, WorkflowDispatch};
|
|||
use crate::tasks::workflows::{
|
||||
release::{ComplianceContext, add_compliance_steps},
|
||||
runners,
|
||||
steps::{self, CommonJobConditions, named},
|
||||
steps::{self, CommonJobConditions, CommonPermissionSets, named},
|
||||
vars::StepOutput,
|
||||
};
|
||||
|
||||
|
|
@ -11,6 +11,7 @@ pub fn compliance_check() -> Workflow {
|
|||
let check = scheduled_compliance_check();
|
||||
|
||||
named::workflow()
|
||||
.with_minimal_permissions()
|
||||
.on(Event::default()
|
||||
.schedule([Schedule::new("30 17 * * 2")])
|
||||
.workflow_dispatch(WorkflowDispatch::default()))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use gh_workflow::*;
|
||||
|
||||
use crate::tasks::workflows::steps::{CommonJobConditions, NamedJob, named};
|
||||
use crate::tasks::workflows::steps::{CommonJobConditions, CommonPermissionSets, NamedJob, named};
|
||||
|
||||
use super::{runners, steps};
|
||||
|
||||
|
|
@ -9,6 +9,7 @@ pub fn danger() -> Workflow {
|
|||
let danger = danger_job();
|
||||
|
||||
named::workflow()
|
||||
.with_minimal_permissions()
|
||||
.on(Event::default()
|
||||
.pull_request(PullRequest::default().add_branch("main").types([
|
||||
PullRequestType::Opened,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ use indoc::indoc;
|
|||
|
||||
use crate::tasks::workflows::runners::{self, Platform};
|
||||
use crate::tasks::workflows::steps::{
|
||||
self, CommonJobConditions, FluentBuilder as _, NamedJob, dependant_job, named, use_clang,
|
||||
self, CommonJobConditions, CommonPermissionSets, FluentBuilder as _, NamedJob, dependant_job,
|
||||
named, use_clang,
|
||||
};
|
||||
use crate::tasks::workflows::vars;
|
||||
|
||||
|
|
@ -14,6 +15,7 @@ pub(crate) fn deploy_collab() -> Workflow {
|
|||
let deploy = deploy(&[&publish]);
|
||||
|
||||
named::workflow()
|
||||
.with_minimal_permissions()
|
||||
.on(Event::default().push(Push::default().add_tag("collab-production")))
|
||||
.add_env(("DOCKER_BUILDKIT", "1"))
|
||||
.add_job(style.name, style.job)
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ use gh_workflow::{
|
|||
use crate::tasks::workflows::{
|
||||
runners,
|
||||
steps::{
|
||||
self, CommonJobConditions, FluentBuilder as _, NamedJob, UploadArtifactStep, named,
|
||||
release_job,
|
||||
self, CommonJobConditions, CommonPermissionSets, FluentBuilder as _, NamedJob,
|
||||
UploadArtifactStep, named, release_job,
|
||||
},
|
||||
vars::{self, StepOutput, WorkflowInput},
|
||||
};
|
||||
|
|
@ -314,6 +314,7 @@ pub(crate) fn deploy_docs() -> Workflow {
|
|||
let deploy_docs = deploy_docs_job(&channel, &checkout_ref);
|
||||
|
||||
named::workflow()
|
||||
.with_minimal_permissions()
|
||||
.add_event(
|
||||
Event::default().workflow_dispatch(
|
||||
WorkflowDispatch::default()
|
||||
|
|
@ -366,6 +367,7 @@ pub(crate) fn deploy_nightly_docs() -> Workflow {
|
|||
|
||||
named::workflow()
|
||||
.name("deploy_nightly_docs")
|
||||
.permissions(Permissions::default())
|
||||
.add_event(Event::default().push(Push::default().add_branch("main")))
|
||||
.add_job(deploy_docs.name, deploy_docs.job)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use crate::tasks::workflows::{
|
|||
extensions::WithAppSecrets,
|
||||
run_tests::DETECT_CHANGED_EXTENSIONS_SCRIPT,
|
||||
runners,
|
||||
steps::{self, CommonJobConditions, NamedJob, named},
|
||||
steps::{self, CommonJobConditions, CommonPermissionSets, NamedJob, named},
|
||||
vars::{StepOutput, one_workflow_per_non_main_branch},
|
||||
};
|
||||
|
||||
|
|
@ -20,6 +20,7 @@ pub(crate) fn extension_auto_bump() -> Workflow {
|
|||
let bump = bump_extension_versions(&detect);
|
||||
|
||||
named::workflow()
|
||||
.with_minimal_permissions()
|
||||
.add_event(
|
||||
Event::default().push(
|
||||
Push::default()
|
||||
|
|
|
|||
|
|
@ -5,9 +5,10 @@ use crate::tasks::workflows::{
|
|||
extension_tests::{self},
|
||||
runners,
|
||||
steps::{
|
||||
self, BASH_SHELL, CommonJobConditions, DEFAULT_REPOSITORY_OWNER_GUARD, GitHubScriptStep,
|
||||
GitRef, NamedJob, RefSha, RepositoryTarget, cache_rust_dependencies_namespace,
|
||||
checkout_repo, create_ref, dependant_job, generate_token, named,
|
||||
self, BASH_SHELL, CommonJobConditions, CommonPermissionSets,
|
||||
DEFAULT_REPOSITORY_OWNER_GUARD, GitHubScriptStep, GitRef, NamedJob, RefSha,
|
||||
RepositoryTarget, cache_rust_dependencies_namespace, checkout_repo, create_ref,
|
||||
dependant_job, generate_token, named,
|
||||
},
|
||||
vars::{
|
||||
JobOutput, StepOutput, WorkflowInput, WorkflowSecret,
|
||||
|
|
@ -58,6 +59,7 @@ pub(crate) fn extension_bump() -> Workflow {
|
|||
);
|
||||
|
||||
named::workflow()
|
||||
.with_minimal_permissions()
|
||||
.add_event(
|
||||
Event::default().workflow_call(
|
||||
WorkflowCall::default()
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use crate::tasks::workflows::{
|
|||
},
|
||||
runners,
|
||||
steps::{
|
||||
self, BASH_SHELL, CommonJobConditions, FluentBuilder, NamedJob,
|
||||
self, BASH_SHELL, CommonJobConditions, CommonPermissionSets, FluentBuilder, NamedJob,
|
||||
cache_rust_dependencies_namespace, named,
|
||||
},
|
||||
vars::{PathCondition, StepOutput, WorkflowInput, one_workflow_per_non_main_branch_and_token},
|
||||
|
|
@ -41,6 +41,7 @@ pub(crate) fn extension_tests() -> Workflow {
|
|||
let working_directory = WorkflowInput::string("working-directory", Some(".".to_owned()));
|
||||
|
||||
named::workflow()
|
||||
.with_minimal_permissions()
|
||||
.add_event(
|
||||
Event::default().workflow_call(
|
||||
WorkflowCall::default()
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ use serde_json::json;
|
|||
use crate::tasks::workflows::steps::GitRef;
|
||||
use crate::tasks::workflows::steps::RefSha;
|
||||
use crate::tasks::workflows::steps::{
|
||||
CheckoutStep, DownloadArtifactStep, IfNoFilesFound, ResultEncoding, TokenPermissions,
|
||||
UploadArtifactStep, cache_rust_dependencies_namespace,
|
||||
CheckoutStep, CommonPermissionSets, DownloadArtifactStep, IfNoFilesFound, ResultEncoding,
|
||||
TokenPermissions, UploadArtifactStep, cache_rust_dependencies_namespace,
|
||||
};
|
||||
use crate::tasks::workflows::vars::JobOutput;
|
||||
use crate::tasks::workflows::{
|
||||
|
|
@ -42,6 +42,7 @@ pub(crate) fn extension_workflow_rollout() -> Workflow {
|
|||
let create_tag = create_rollout_tag(&rollout_workflows, &filter_repos_input);
|
||||
|
||||
named::workflow()
|
||||
.with_minimal_permissions()
|
||||
.on(Event::default().workflow_dispatch(
|
||||
WorkflowDispatch::default()
|
||||
.add_input(filter_repos_input.name, filter_repos_input.input())
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ pub(crate) fn bump_version(args: &GenerateWorkflowArgs) -> Workflow {
|
|||
)
|
||||
.pull_request(PullRequest::default().add_type(PullRequestType::Labeled))
|
||||
.workflow_dispatch(WorkflowDispatch::default()))
|
||||
.permissions(Permissions::default())
|
||||
.concurrency(one_workflow_per_non_main_branch_and_token("labels"))
|
||||
.add_job(determine_bump_type.name, determine_bump_type.job)
|
||||
.add_job(call_bump_version.name, call_bump_version.job)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use crate::tasks::workflows::{
|
|||
pub(crate) fn run_tests(args: &GenerateWorkflowArgs) -> Workflow {
|
||||
let call_extension_tests = call_extension_tests(args.sha.as_ref());
|
||||
named::workflow()
|
||||
.permissions(Permissions::default().contents(Level::Read))
|
||||
.on(Event::default()
|
||||
.pull_request(PullRequest::default().add_branch("**"))
|
||||
.push(Push::default().add_branch("main")))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::tasks::workflows::{
|
||||
runners::{Arch, Platform},
|
||||
steps::{CommonJobConditions, DEFAULT_REPOSITORY_OWNER_GUARD, NamedJob},
|
||||
steps::{CommonJobConditions, CommonPermissionSets, DEFAULT_REPOSITORY_OWNER_GUARD, NamedJob},
|
||||
};
|
||||
|
||||
use super::{runners, steps, steps::named, vars};
|
||||
|
|
@ -13,6 +13,7 @@ use gh_workflow::*;
|
|||
pub fn nix_build() -> Workflow {
|
||||
let [nix_linux_x86_64, nix_mac_aarch64] = nix_pr_jobs(&["run-nix", "run-bundling"]);
|
||||
named::workflow()
|
||||
.with_minimal_permissions()
|
||||
.on(Event::default().pull_request(
|
||||
PullRequest::default().types([PullRequestType::Labeled, PullRequestType::Synchronize]),
|
||||
))
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ use indoc::{formatdoc, indoc};
|
|||
use crate::tasks::workflows::{
|
||||
runners,
|
||||
steps::{
|
||||
self, DEFAULT_REPOSITORY_OWNER_GUARD, GitRef, NamedJob, RefSha, RepositoryTarget,
|
||||
TokenPermissions, generate_token, named,
|
||||
self, CommonPermissionSets, DEFAULT_REPOSITORY_OWNER_GUARD, GitRef, NamedJob, RefSha,
|
||||
RepositoryTarget, TokenPermissions, generate_token, named,
|
||||
},
|
||||
vars::{self, StepOutput, WorkflowInput},
|
||||
};
|
||||
|
|
@ -22,6 +22,7 @@ pub fn publish_extension_cli() -> Workflow {
|
|||
let update_sha_in_extensions = update_sha_in_extensions(&publish, &message);
|
||||
|
||||
named::workflow()
|
||||
.with_minimal_permissions()
|
||||
.on(Event::default().workflow_dispatch(
|
||||
WorkflowDispatch::default().add_input(message.name, message.input()),
|
||||
))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
use gh_workflow::{Event, Expression, Level, Push, Run, Step, Use, Workflow, ctx::Context};
|
||||
use gh_workflow::{
|
||||
Event, Expression, Level, Permissions, Push, Run, Step, Use, Workflow, ctx::Context,
|
||||
};
|
||||
use indoc::formatdoc;
|
||||
|
||||
use crate::tasks::workflows::{
|
||||
|
|
@ -6,8 +8,8 @@ use crate::tasks::workflows::{
|
|||
run_tests,
|
||||
runners::{self, Arch, Platform},
|
||||
steps::{
|
||||
self, DownloadArtifactStep, FluentBuilder, NamedJob, TokenPermissions, dependant_job,
|
||||
named, release_job,
|
||||
self, CommonPermissionSets, DownloadArtifactStep, FluentBuilder, NamedJob,
|
||||
TokenPermissions, dependant_job, named, release_job,
|
||||
},
|
||||
vars::{self, JobOutput, StepOutput, assets},
|
||||
};
|
||||
|
|
@ -101,6 +103,7 @@ pub(crate) fn release() -> Workflow {
|
|||
named::workflow()
|
||||
.on(Event::default().push(Push::default().tags(vec!["v*".to_string()])))
|
||||
.concurrency(vars::one_workflow_per_non_main_branch())
|
||||
.with_minimal_permissions()
|
||||
.add_env(("CARGO_TERM_COLOR", "always"))
|
||||
.add_env(("RUST_BACKTRACE", "1"))
|
||||
.add_job(macos_tests.name, macos_tests.job)
|
||||
|
|
@ -466,6 +469,7 @@ fn upload_release_assets(deps: &[&NamedJob], bundle: &ReleaseBundleJobs) -> Name
|
|||
named::job(
|
||||
dependant_job(&deps)
|
||||
.runs_on(runners::LINUX_MEDIUM)
|
||||
.permissions(Permissions::default().contents(Level::Write))
|
||||
.add_step(download_workflow_artifacts())
|
||||
.add_step(steps::script("ls -lR ./artifacts"))
|
||||
.add_step(prep_release_artifacts())
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ use crate::tasks::workflows::{
|
|||
run_tests::run_platform_tests_no_filter,
|
||||
runners::{Arch, Platform, ReleaseChannel},
|
||||
steps::{
|
||||
CommonJobConditions, DEFAULT_REPOSITORY_OWNER_GUARD, FluentBuilder, GitRef, NamedJob,
|
||||
RefSha, RepositoryTarget, TokenPermissions,
|
||||
CommonJobConditions, CommonPermissionSets, DEFAULT_REPOSITORY_OWNER_GUARD, FluentBuilder,
|
||||
GitRef, NamedJob, RefSha, RepositoryTarget, TokenPermissions,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -47,6 +47,7 @@ pub fn release_nightly() -> Workflow {
|
|||
let notify_on_failure = notify_on_failure(&bundle.jobs());
|
||||
|
||||
named::workflow()
|
||||
.with_minimal_permissions()
|
||||
.on(Event::default()
|
||||
// Fire 6 times a day
|
||||
.schedule([Schedule::new("0 */4 * * *")])
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@ use std::path::Path;
|
|||
use crate::tasks::workflows::{
|
||||
release::ReleaseBundleJobs,
|
||||
runners::{Arch, Platform, ReleaseChannel},
|
||||
steps::{FluentBuilder, IfNoFilesFound, NamedJob, UploadArtifactStep, dependant_job, named},
|
||||
steps::{
|
||||
CommonPermissionSets, FluentBuilder, IfNoFilesFound, NamedJob, UploadArtifactStep,
|
||||
dependant_job, named,
|
||||
},
|
||||
vars::{self, assets, bundle_envs},
|
||||
};
|
||||
|
||||
|
|
@ -23,6 +26,7 @@ pub fn run_bundling() -> Workflow {
|
|||
windows_x86_64: bundle_windows(Arch::X86_64, None, &[]),
|
||||
};
|
||||
named::workflow()
|
||||
.with_minimal_permissions()
|
||||
.on(Event::default().pull_request(
|
||||
PullRequest::default().types([PullRequestType::Labeled, PullRequestType::Synchronize]),
|
||||
))
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ use serde_json::json;
|
|||
|
||||
use crate::tasks::workflows::{
|
||||
steps::{
|
||||
CommonJobConditions, cache_rust_dependencies_namespace, repository_owner_guard_expression,
|
||||
use_clang,
|
||||
CommonJobConditions, CommonPermissionSets, cache_rust_dependencies_namespace,
|
||||
repository_owner_guard_expression, use_clang,
|
||||
},
|
||||
vars::{self, PathCondition},
|
||||
};
|
||||
|
|
@ -105,6 +105,7 @@ pub(crate) fn run_tests() -> Workflow {
|
|||
); // could be more specific here?
|
||||
|
||||
named::workflow()
|
||||
.with_minimal_permissions()
|
||||
.add_event(
|
||||
Event::default()
|
||||
.push(
|
||||
|
|
|
|||
|
|
@ -376,6 +376,16 @@ impl CommonJobConditions for Job {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait CommonPermissionSets: Sized {
|
||||
fn with_minimal_permissions(self) -> Self;
|
||||
}
|
||||
|
||||
impl CommonPermissionSets for Workflow {
|
||||
fn with_minimal_permissions(self) -> Self {
|
||||
self.permissions(Permissions::default().contents(Level::Read))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn release_job(deps: &[&NamedJob]) -> Job {
|
||||
dependant_job(deps)
|
||||
.with_repository_owner_guard()
|
||||
|
|
@ -503,6 +513,7 @@ pub mod named {
|
|||
.collect::<Vec<_>>()
|
||||
.join("::"),
|
||||
)
|
||||
.permissions(Permissions::default())
|
||||
.defaults(Defaults::default().run(RunDefaults::default().shell(BASH_SHELL)))
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue