actions_github/
context.rs

1//! Context related utilities
2//!
3//! This is the object that the worker inject into your application.
4//!
5//! You may want to check out [Context] to find the available variable
6//! and GitHub's documentation.
7//!
8//! To obtain the context, use the method [get_context] which hydrates the object
9//! with all the values from the environment variables
10use std::{env, fs, path::Path};
11
12use crate::error::ActionsError;
13use json::JsonValue;
14
15/// Context class injected by the action worker.
16///
17/// Find detailed documentation in
18/// [GitHub's documentation for Contexts](https://docs.github.com/en/actions/learn-github-actions/contexts#github-context)
19#[derive(Debug, Clone)]
20pub struct Context {
21    pub payload: JsonValue,
22    pub event_name: String,
23    pub sha: String,
24    pub ref_: String,
25    pub workflow: String,
26    pub action: String,
27    pub actor: String,
28    pub job: String,
29    pub run_attempt: u8,
30    pub run_number: u8,
31    pub run_id: u128,
32    pub api_url: String,
33    pub server_url: String,
34    pub graphql_url: String,
35    pub repo: Repo,
36}
37
38#[derive(Debug, Clone)]
39/// Container of two small values, owner and repo
40pub struct Repo {
41    pub owner: String,
42    pub repo: String,
43}
44
45/// Gets environment variables provided by GitHub and injects them into an object
46///
47/// Returns a [Context] object
48///
49/// Could return an [ActionsError] error type.
50/// ```rust,no_run
51/// use actions_github::context::get_context;
52/// let data = get_context().unwrap();
53/// println!("Event is {}", data.event_name);
54/// ```
55pub fn get_context() -> Result<Context, ActionsError> {
56    let mut payload: JsonValue = JsonValue::Null;
57
58    if let Ok(github_event_path) = env::var("GITHUB_EVENT_PATH") {
59        if Path::new(&github_event_path).exists() {
60            match fs::read_to_string(&github_event_path) {
61                Ok(content) => match json::parse(&content) {
62                    Ok(parsed_json) => payload = parsed_json,
63                    Err(err) => println!("Failed to parse JSON: {}", err),
64                },
65                Err(err) => println!("Failed to read file: {}", err),
66            }
67        } else {
68            println!("GITHUB_EVENT_PATH {} does not exist", github_event_path)
69        }
70    }
71
72    let repo: Repo = get_repo(&payload)?;
73
74    Ok(Context {
75        payload,
76        event_name: get_env("GITHUB_EVENT_NAME")?,
77        sha: get_env("GITHUB_SHA")?,
78        ref_: get_env("GITHUB_REF")?,
79        workflow: get_env("GITHUB_WORKFLOW")?,
80        action: get_env("GITHUB_ACTION")?,
81        actor: get_env("GITHUB_ACTOR")?,
82        job: get_env("GITHUB_JOB")?,
83        run_attempt: get_env("GITHUB_RUN_ATTEMPT")?.parse::<u8>().unwrap(),
84        run_number: get_env("GITHUB_RUN_NUMBER")?.parse::<u8>().unwrap(),
85        run_id: get_env("GITHUB_RUN_ID")?.parse::<u128>().unwrap(),
86        api_url: get_env_or("GITHUB_API_URL", "https://api.github.com"),
87        server_url: get_env_or("GITHUB_SERVER_URL", "https://github.com"),
88        graphql_url: get_env_or("GITHUB_GRAPHQL_URL", "https://api.github.com/graphql"),
89        repo,
90    })
91}
92
93fn get_env(var_name: &str) -> Result<String, ActionsError> {
94    match env::var(var_name) {
95        Ok(var) => Ok(var),
96        Err(_) => Err(ActionsError::Context(var_name.to_string())),
97    }
98}
99
100fn get_env_or(var_name: &str, default: &str) -> String {
101    match env::var(var_name) {
102        Ok(var) => var,
103        Err(_) => default.to_string(),
104    }
105}
106
107fn get_repo(payload: &JsonValue) -> Result<Repo, ActionsError> {
108    // We try to get it from the environment variable
109    if let Ok(repository) = env::var("GITHUB_REPOSITORY") {
110        let owner_repo = repository.split('/').collect::<Vec<&str>>();
111
112        return Ok(Repo {
113            owner: owner_repo[0].to_string(),
114            repo: owner_repo[1].to_string(),
115        });
116    }
117
118    let owner = payload["repository"]["login"]["login"].clone();
119    let repo = payload["repository"]["name"].clone();
120
121    if owner.is_null() || repo.is_null() {
122        Err(ActionsError::Context(
123            "context.repo requires a GITHUB_REPOSITORY environment variable like 'owner/repo'"
124                .to_string(),
125        ))
126    } else {
127        Ok(Repo {
128            owner: owner.to_string(),
129            repo: repo.to_string(),
130        })
131    }
132}