1#![allow(dead_code, unused_imports)]
3#![deny(missing_docs)]
4
5#[cfg(feature = "log")]
6extern crate log;
7
8use std::{
9 io::Write,
10 path::{Path, PathBuf},
11};
12
13pub mod actions;
14pub mod errors;
15#[cfg(feature = "log")]
16pub mod logging;
17pub mod repository;
18
19pub use crate::actions::models::{ActionInput, ActionRuns, ActionYML};
20pub use crate::errors::ActionsError;
21pub use crate::repository::reference::RepositoryReference;
22
23pub trait ActionTrait {
25 fn init() -> Result<Self, ActionsError>
27 where
28 Self: Sized;
29
30 fn name(&self) -> &str;
32
33 fn description(&self) -> &str;
35
36 fn get_input(key: impl Into<String> + Copy) -> Result<String, ActionsError> {
38 let key = key.into();
39 std::env::var(&key).map_err(|_| ActionsError::InputError(key))
40 }
41
42 fn get_input_bool(key: impl Into<String> + Copy) -> Result<bool, ActionsError> {
44 Self::get_input(key)?
45 .parse::<bool>()
46 .map_err(|_| ActionsError::InputTypeError(key.into(), "bool".into()))
47 }
48
49 fn get_input_int(key: impl Into<String> + Copy) -> Result<i32, ActionsError> {
51 Self::get_input(key)?
52 .parse::<i32>()
53 .map_err(|_| ActionsError::InputTypeError(key.into(), "int".into()))
54 }
55
56 fn get_input_vec(
58 key: impl Into<String> + Copy,
59 seperator: &str,
60 ) -> Result<Vec<String>, ActionsError> {
61 Ok(Self::get_input(key)?
62 .split(seperator)
63 .map(|s| s.to_string())
64 .collect::<Vec<String>>())
65 }
66
67 fn set_output(key: impl Into<String>, value: impl Into<String>) -> Result<(), ActionsError> {
69 let key = key.into();
70 let value = value.into();
71
72 let output_file = Self::get_output_path();
73 let output_path = PathBuf::from(output_file.clone());
74
75 if !output_path.exists() {
76 #[cfg(feature = "log")]
77 log::debug!("Creating output file: {}", output_path.display());
78 std::fs::File::create(&output_path)?;
79 }
80
81 match std::fs::OpenOptions::new().append(true).open(output_file) {
82 Ok(mut file) => {
83 writeln!(file, "{key}={value}")?;
84 }
85 Err(e) => {
86 #[cfg(feature = "log")]
87 log::error!("Failed to open output file: {e}");
88
89 println!("::set-output name={key}::{value}");
91 }
92 }
93
94 Ok(())
95 }
96
97 #[cfg(feature = "octocrab")]
101 fn octocrab(&self) -> Result<octocrab::Octocrab, ActionsError> {
102 #[cfg(feature = "log")]
103 {
104 log::debug!("Creating Octocrab instance");
105 log::debug!("URL: {}", self.get_api_url());
106 }
107
108 match self.get_token() {
109 Ok(token) => self.octocrab_with_token(token),
110 Err(_) => {
111 #[cfg(feature = "log")]
112 log::warn!("No GitHub Token provided");
113
114 self.octocrab_without_token()
115 }
116 }
117 }
118
119 #[cfg(feature = "octocrab")]
121 fn octocrab_with_token(
122 &self,
123 token: impl Into<String>,
124 ) -> Result<octocrab::Octocrab, ActionsError> {
125 let token = token.into();
126 #[cfg(feature = "log")]
127 log::debug!("Creating Octocrab instance with token");
128
129 if token.is_empty() {
130 return Err(ActionsError::OctocrabError(
131 "Token cannot be empty".to_string(),
132 ));
133 }
134
135 octocrab::Octocrab::builder()
136 .base_uri(self.get_api_url())
137 .map_err(|e| ActionsError::OctocrabError(e.to_string()))?
138 .add_header(
139 http::header::ACCEPT,
140 "application/vnd.github.v3+json".to_string(),
141 )
142 .personal_token(token)
143 .build()
144 .map_err(|e| ActionsError::OctocrabError(e.to_string()))
145 }
146
147 #[cfg(feature = "octocrab")]
149 fn octocrab_without_token(&self) -> Result<octocrab::Octocrab, ActionsError> {
150 #[cfg(feature = "log")]
151 log::debug!("Creating Octocrab instance without token");
152
153 octocrab::Octocrab::builder()
154 .base_uri(self.get_api_url())
155 .map_err(|e| ActionsError::OctocrabError(e.to_string()))?
156 .add_header(
157 http::header::ACCEPT,
158 "application/vnd.github.v3+json".to_string(),
159 )
160 .build()
161 .map_err(|e| ActionsError::OctocrabError(e.to_string()))
162 }
163
164 fn get_output_path() -> String {
168 if let Ok(ghout) = std::env::var("GITHUB_OUTPUT") {
169 #[cfg(feature = "log")]
170 log::debug!("GITHUB_OUTPUT: {ghout}");
171 ghout
172 } else if let Ok(ghout) = std::env::var("GITHUB_STATE") {
173 #[cfg(feature = "log")]
174 log::debug!("GITHUB_STATE: {ghout}");
175 ghout
176 } else {
177 #[cfg(feature = "log")]
178 log::debug!("Default Output: /tmp/github_actions.env");
179 "/tmp/github_actions.env".to_string()
180 }
181 }
182
183 fn get_server_url(&self) -> String {
185 Self::get_input("GITHUB_SERVER_URL").unwrap_or_else(|_| "https://github.com".into())
186 }
187 fn get_api_url(&self) -> String {
189 Self::get_input("GITHUB_API_URL").unwrap_or_else(|_| "https://api.github.com".into())
190 }
191 fn get_graphql_url(&self) -> String {
193 Self::get_input("GITHUB_GRAPHQL_URL")
194 .unwrap_or_else(|_| "https://api.github.com/graphql".into())
195 }
196
197 fn get_token(&self) -> Result<String, ActionsError> {
201 Self::get_input("GITHUB_TOKEN").or_else(|_| Self::get_input("ACTIONS_RUNTIME_TOKEN"))
202 }
203 fn get_sha(&self) -> Result<String, ActionsError> {
205 Self::get_input("GITHUB_SHA")
206 }
207 fn get_ref(&self) -> Result<String, ActionsError> {
209 Self::get_input("GITHUB_REF")
210 }
211 fn get_ref_type(&self) -> Result<String, ActionsError> {
213 Self::get_input("GITHUB_REF_TYPE")
214 }
215 fn get_ref_name(&self) -> Result<String, ActionsError> {
217 Self::get_input("GITHUB_REF_NAME")
218 }
219
220 fn get_event_name(&self) -> Result<String, ActionsError> {
222 Self::get_input("GITHUB_EVENT_NAME")
223 }
224
225 fn get_repository(&self) -> Result<String, ActionsError> {
227 Self::get_input("GITHUB_REPOSITORY")
228 }
229 fn get_repository_owner(&self) -> Result<String, ActionsError> {
231 Self::get_input("GITHUB_REPOSITORY_OWNER").or_else(|_| {
232 self.get_repository()
233 .map(|r| r.split('/').collect::<Vec<&str>>()[0].to_string())
234 })
235 }
236 fn get_repository_name(&self) -> Result<String, ActionsError> {
238 self.get_repository()
239 .map(|r| r.split('/').collect::<Vec<&str>>()[1].to_string())
240 }
241 fn get_repository_url(&self) -> Result<String, ActionsError> {
243 Self::get_input("GITHUB_REPOSITORYURL")
244 }
245 fn get_actor(&self) -> Result<String, ActionsError> {
247 Self::get_input("GITHUB_ACTOR")
248 }
249}