Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: type safe job dependency #70

Merged
merged 2 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ jobs:
- name: Cargo Clippy
run: cargo +nightly clippy --all-features --workspace -- -D warnings
release:
needs: build
needs:
- build
name: Release
runs-on: ubuntu-latest
permissions:
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

64 changes: 32 additions & 32 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::is_default;

/// Represents all possible webhook events that can trigger a workflow
/// See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows
#[derive(Default, Debug, Clone, Deserialize, Serialize, Merge, Setters)]
#[derive(Default, Debug, Clone, Deserialize, Serialize, Merge, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct Event {
#[serde(skip_serializing_if = "Option::is_none")]
Expand Down Expand Up @@ -93,7 +93,7 @@ pub enum BranchProtectionRuleType {
}

/// Configuration for branch protection rule events
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct BranchProtectionRule {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
Expand Down Expand Up @@ -123,7 +123,7 @@ pub enum CheckRunType {
RequestedAction,
}

#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct CheckRun {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
Expand All @@ -149,7 +149,7 @@ pub enum CheckSuiteType {

/// Configuration for check suite events

#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct CheckSuite {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
Expand All @@ -166,7 +166,7 @@ impl CheckSuite {

/// Configuration for create events (branch or tag creation)
/// See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#create
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct Create {
/// Filter on specific branch names
Expand All @@ -193,7 +193,7 @@ impl Create {

/// Configuration for delete events (branch or tag deletion)
/// See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#delete
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct Delete {
/// Filter on specific branch names
Expand Down Expand Up @@ -221,7 +221,7 @@ impl Delete {
/// Types of deployment events
/// See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#deployment

#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct Deployment {
/// Filter on specific branch names
Expand All @@ -240,7 +240,7 @@ impl Deployment {
/// Types of deployment status events
/// See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#deployment_status

#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct DeploymentStatus {
/// Filter on specific deployment states
Expand Down Expand Up @@ -291,7 +291,7 @@ pub enum DiscussionType {

/// Configuration for discussion events

#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
pub struct Discussion {
/// Filter on specific discussion event types
#[serde(default, skip_serializing_if = "Vec::is_empty")]
Expand Down Expand Up @@ -321,7 +321,7 @@ pub enum DiscussionCommentType {

/// Configuration for discussion comment events

#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
pub struct DiscussionComment {
/// Filter on specific discussion comment event types
#[serde(default, skip_serializing_if = "Vec::is_empty")]
Expand All @@ -338,7 +338,7 @@ impl DiscussionComment {

/// Configuration for issue comment events
/// See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#issue_comment
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
pub struct IssueComment {
/// Filter on specific issue comment event types
#[serde(default, skip_serializing_if = "Vec::is_empty")]
Expand Down Expand Up @@ -406,7 +406,7 @@ pub enum IssuesType {

/// Configuration for issue events

#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct Issues {
/// Filter on specific issue event types
Expand Down Expand Up @@ -437,7 +437,7 @@ pub enum LabelType {

/// Configuration for label events

#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct Label {
/// Filter on specific label event types
Expand All @@ -459,7 +459,7 @@ pub enum MergeGroupType {
ChecksRequested,
}

#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct MergeGroup {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
Expand All @@ -483,7 +483,7 @@ pub enum MilestoneType {
Deleted,
}

#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct Milestone {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
Expand Down Expand Up @@ -516,7 +516,7 @@ pub enum PullRequestType {
ReviewRequestRemoved,
}

#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct PullRequest {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
Expand Down Expand Up @@ -559,7 +559,7 @@ pub enum PullRequestReviewType {

/// Configuration for pull request review events

#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct PullRequestReview {
/// Filter on specific pull request review event types
Expand Down Expand Up @@ -589,7 +589,7 @@ pub enum PullRequestReviewCommentType {
}

/// Configuration for pull request review comment events
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct PullRequestReviewComment {
/// Filter on specific pull request review comment event types
Expand All @@ -607,7 +607,7 @@ impl PullRequestReviewComment {

/// Configuration for pull request target events
/// See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct PullRequestTarget {
/// Filter on specific pull request event types
Expand All @@ -634,7 +634,7 @@ impl PullRequestTarget {

/// Configuration for push events
/// See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct Push {
/// Filter on specific branch names
Expand Down Expand Up @@ -672,7 +672,7 @@ pub enum RegistryPackageType {

/// Configuration for registry package events

#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct RegistryPackage {
/// Filter on specific registry package event types
Expand Down Expand Up @@ -711,7 +711,7 @@ pub enum ReleaseType {

/// Configuration for release events

#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct Release {
/// Filter on specific release event types
Expand All @@ -727,7 +727,7 @@ impl Release {
}
}

#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct RepositoryDispatch {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
Expand All @@ -741,7 +741,7 @@ impl RepositoryDispatch {
}
}

#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct Schedule {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
Expand All @@ -755,7 +755,7 @@ impl Schedule {
}
}

#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct Watch {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
Expand All @@ -771,7 +771,7 @@ impl Watch {

/// Configuration for workflow call events
/// See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_call
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct WorkflowCall {
/// Inputs for the workflow call
Expand All @@ -786,7 +786,7 @@ pub struct WorkflowCall {
}

/// Configuration for workflow call input
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Setters, Eq)]
#[setters(strip_option, into)]
pub struct WorkflowCallInput {
/// Description of the input
Expand All @@ -804,7 +804,7 @@ pub struct WorkflowCallInput {
}

/// Configuration for workflow call output
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Setters, Eq)]
#[setters(strip_option, into)]
pub struct WorkflowCallOutput {
/// Description of the output
Expand All @@ -816,7 +816,7 @@ pub struct WorkflowCallOutput {
}

/// Configuration for workflow call secret
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Setters, Eq)]
#[setters(strip_option, into)]
pub struct WorkflowCallSecret {
/// Description of the secret
Expand All @@ -830,7 +830,7 @@ pub struct WorkflowCallSecret {
/// Configuration for workflow dispatch events
/// See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch

#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct WorkflowDispatch {
/// Inputs for the workflow dispatch
Expand All @@ -839,7 +839,7 @@ pub struct WorkflowDispatch {
}

/// Configuration for workflow dispatch input
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct WorkflowDispatchInput {
/// Description of the input
Expand Down Expand Up @@ -870,7 +870,7 @@ pub enum WorkflowRunType {
}

/// Configuration for workflow run events
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, Setters, PartialEq, Eq)]
#[setters(strip_option, into)]
pub struct WorkflowRun {
/// Filter on specific workflow run event types
Expand Down
49 changes: 48 additions & 1 deletion src/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ use std::path::PathBuf;
use std::process::Command;

use derive_setters::Setters;
use indexmap::IndexMap;

use crate::error::{Error, Result};
use crate::Workflow;
use crate::{Job, Jobs, Workflow};

#[derive(Setters, Clone)]
#[setters(into)]
Expand All @@ -16,6 +17,7 @@ pub struct Generate {

impl Generate {
pub fn new(workflow: Workflow) -> Self {
let workflow = organize_job_dependency(workflow);
Self { workflow, name: "ci.yml".to_string() }
}

Expand Down Expand Up @@ -78,3 +80,48 @@ impl Generate {
}
}
}

/// Organizes job dependencies within a given `Workflow`.
///
/// This function iterates over all jobs in the provided `Workflow` and ensures that
/// each job's dependencies are correctly set up. If a job has dependencies specified
/// in `tmp_needs`, it checks if those dependencies are already defined in the workflow.
/// If not, it creates new job IDs for the missing dependencies and inserts them into
/// the workflow. The function then updates the `needs` field of each job with the
/// appropriate job IDs.
fn organize_job_dependency(mut workflow: Workflow) -> Workflow {
let mut job_id = 0;
let mut new_jobs = IndexMap::<String, Job>::new();

// Iterate over all jobs
for (id, mut job) in workflow.jobs.clone().unwrap_or_default().0.into_iter() {
// If job has dependencies
if let Some(dep_jobs) = &job.tmp_needs {
// Prepare the job_ids
let mut job_ids = Vec::<String>::new();
for dep_job in dep_jobs.into_iter() {
// If the job is already defined in the workflow
if let Some(id) = workflow.get_id(&dep_job) {
job_ids.push(id.to_string());
} else {
// Create a job-id for the job
let id = format!("job-{}", job_id);
job_id += 1;

// Add job id as the dependency
job_ids.push(id.clone());

// Insert the missing job into the new_jobs
new_jobs.insert(format!("job-{}", job_id), dep_job.clone());
}
}
job.needs = Some(job_ids);
}

new_jobs.insert(id.clone(), job.clone());
}

workflow.jobs = Some(Jobs(new_jobs));

workflow
}
Loading