Skip to main content

ephemeris_forge_github/operation/
actions.rs

1use std::fmt;
2
3use ephemeris_forge::{FieldDiff, Limitation};
4
5use crate::config::actions::{
6    Access, AllowList, ArtifactAndLogRetention, Cache, ForkPrContributorApproval,
7    ForkPrWorkflowsPrivateRepos, OidcSubject, Permissions, Workflow,
8};
9
10macro_rules! groups {
11    (
12        $($(#[$meta:meta])* $variant:ident($type:ty) = $name:literal [$($field:ident),+]
13            $(, limitation = $limitation:expr)?;)+
14    ) => {
15        /// One whole Actions settings group.
16        ///
17        /// As a write it replaces the group. As live state it is what GitHub
18        /// reports.
19        #[derive(Clone, Debug, Eq, PartialEq)]
20        pub enum Group {
21            $($(#[$meta])* $variant($type),)+
22        }
23
24        impl fmt::Display for Group {
25            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26                let name = match self {
27                    $(Self::$variant(_) => $name,)+
28                };
29                write!(f, "actions {name}")
30            }
31        }
32
33        $(
34            impl From<$type> for Group {
35                fn from(group: $type) -> Self {
36                    Self::$variant(group)
37                }
38            }
39
40            impl GroupType for $type {
41                const NAME: &'static str = $name;
42
43                fn compare(&self, live: &Self, diff: &mut FieldDiff) {
44                    $(diff.compare(stringify!($field), Some(&live.$field), &self.$field);)+
45                }
46
47                $(fn limitation() -> Limitation {
48                    $limitation
49                })?
50
51                fn pick(group: &Group) -> Option<&Self> {
52                    if let Group::$variant(found) = group {
53                        Some(found)
54                    } else {
55                        None
56                    }
57                }
58            }
59        )+
60    };
61}
62
63/// One Actions settings group, as a write and as live state.
64pub trait GroupType: Clone + Into<Group> {
65    /// The group's name in the report and in the configuration.
66    const NAME: &'static str;
67
68    /// Compare the declared group with the live one.
69    fn compare(&self, live: &Self, diff: &mut FieldDiff);
70
71    /// What the report says when the live group cannot be read.
72    #[must_use]
73    fn limitation() -> Limitation {
74        Limitation::ActionsUnavailable { group: Self::NAME }
75    }
76
77    /// The group of this type in `group`, if it is one.
78    fn pick(group: &Group) -> Option<&Self>;
79}
80
81groups! {
82    /// Whether Actions runs and which actions may run.
83    Permissions(Permissions) = "permissions" [enabled, allowed_actions, sha_pinning_required];
84    /// The allow list of selected actions.
85    AllowList(AllowList) = "selected_actions"
86        [github_owned_allowed, verified_allowed, patterns_allowed];
87    /// The default workflow token permissions.
88    Workflow(Workflow) = "workflow"
89        [default_workflow_permissions, can_approve_pull_request_reviews];
90    /// Who may use the actions of a private repository.
91    Access(Access) = "access" [access_level];
92    /// The artifact and log retention.
93    ArtifactAndLogRetention(ArtifactAndLogRetention) = "artifact_and_log_retention" [days];
94    /// The fork pull request approval policy.
95    ForkPrContributorApproval(ForkPrContributorApproval) = "fork_pr_contributor_approval"
96        [approval_policy];
97    /// How workflows run for fork pull requests.
98    ForkPrWorkflowsPrivateRepos(ForkPrWorkflowsPrivateRepos) = "fork_pr_workflows_private_repos"
99        [
100            run_workflows_from_fork_pull_requests,
101            send_write_tokens_to_workflows,
102            send_secrets_and_variables,
103            require_approval_for_fork_pr_workflows
104        ],
105        limitation = Limitation::ActionsPrivateOnly {
106            group: "fork_pr_workflows_private_repos"
107        };
108    /// The cache limits.
109    Cache(Cache) = "cache" [max_cache_retention_days, max_cache_size_gb];
110    /// The OIDC subject claim settings.
111    OidcSubject(OidcSubject) = "oidc_subject"
112        [use_default, include_claim_keys, use_immutable_subject];
113}