1+ #!/usr/bin/env python3
2+ """
3+ GitHub Action script to check if an artifact exists in the repository for a specific commit.
4+ Uses the GitHub REST API to list artifacts by name and filters by head SHA.
5+ Returns true if the artifact exists, false otherwise.
6+ """
7+
8+ import os
9+ import sys
10+ import requests
11+ import json
12+
13+ def get_github_token () -> str :
14+ """Get GitHub token from environment variables."""
15+ token = os .getenv ('GITHUB_TOKEN' )
16+ if not token :
17+ print ("::error::GITHUB_TOKEN environment variable is required" )
18+ sys .exit (1 )
19+ return token
20+
21+ def get_head_sha () -> str :
22+ """Get the head SHA from environment variables."""
23+ head_sha = os .getenv ('GITHUB_SHA' )
24+ if not head_sha :
25+ print ("::error::GITHUB_SHA environment variable is required" )
26+ sys .exit (1 )
27+ return head_sha
28+
29+ def get_repository () -> str :
30+ """Get the repository name from environment variables."""
31+ repo = os .getenv ('GITHUB_REPOSITORY' )
32+ if not repo :
33+ print ("::error::GITHUB_REPOSITORY environment variable is required" )
34+ sys .exit (1 )
35+ return repo
36+
37+ def set_output (name : str , value : str ) -> None :
38+ """Set GitHub Actions output."""
39+ github_output = os .getenv ('GITHUB_OUTPUT' )
40+ if github_output :
41+ with open (github_output , 'a' ) as f :
42+ f .write (f"{ name } ={ value } \n " )
43+ else :
44+ # Fallback for older GitHub Actions runner versions
45+ print (f"::set-output name={ name } ::{ value } " )
46+
47+ def check_artifact_exists (artifact_name : str , token : str , repo : str , head_sha : str ) -> bool :
48+ """
49+ Check if an artifact exists in the repository for a specific commit.
50+
51+ Args:
52+ artifact_name: Name of the artifact to check
53+ token: GitHub token for authentication
54+ repo: Repository in format 'owner/repo'
55+ head_sha: Commit SHA to filter artifacts by
56+
57+ Returns:
58+ True if artifact exists, False otherwise
59+ """
60+ headers = {
61+ 'Authorization' : f'token { token } ' ,
62+ 'Accept' : 'application/vnd.github+json' ,
63+ 'X-GitHub-Api-Version' : '2022-11-28'
64+ }
65+
66+ # GitHub API endpoint to list artifacts for a repository
67+ url = f"https://api.github.com/repos/{ repo } /actions/artifacts"
68+
69+ # Add query parameters - API will filter by artifact name
70+ params = {
71+ 'per_page' : 100 ,
72+ 'name' : artifact_name
73+ }
74+
75+ max_iterations = 100
76+
77+ try :
78+ page = 1
79+ while max_iterations > 0 :
80+ params ['page' ] = page
81+ response = requests .get (url , headers = headers , params = params )
82+ response .raise_for_status ()
83+
84+ artifacts_data = response .json ()
85+ artifacts = artifacts_data .get ('artifacts' , [])
86+ total_count = artifacts_data .get ('total_count' , 0 )
87+
88+ # No artifacts found at all
89+ if not artifacts :
90+ print (f"::debug::No artifacts found on page '{ page } ' with name { artifact_name } matching head_sha { head_sha } " )
91+ break
92+
93+ # Check each artifact's head_sha since API filters by name already
94+ for artifact in artifacts :
95+ workflow_run = artifact .get ('workflow_run' , {})
96+ artifact_head_sha = workflow_run .get ('head_sha' )
97+
98+ if artifact_head_sha == head_sha :
99+ print (f"::debug::Artifact '{ artifact_name } ' found with ID { artifact .get ('id' )} , matching head_sha { head_sha } " )
100+ return True
101+ else :
102+ print (f"::debug::Artifact '{ artifact_name } ' found but head_sha doesn't match (expected: { head_sha } , found: { artifact_head_sha } )" )
103+
104+ # Check if we've reached the last page
105+ # If we've retrieved all artifacts (page * per_page >= total_count) or
106+ # if this page has fewer artifacts than requested, we're done
107+ if page * params ['per_page' ] >= total_count or len (artifacts ) < params ['per_page' ]:
108+ break
109+
110+ page += 1
111+ max_iterations -= 1
112+
113+ print (f"::debug::Artifact '{ artifact_name } ' with head_sha '{ head_sha } ' not found" )
114+ return False
115+
116+ except requests .exceptions .RequestException as e :
117+ print (f"::error::Failed to check artifact existence: { e } " )
118+ sys .exit (1 )
119+ except json .JSONDecodeError as e :
120+ print (f"::error::Failed to parse GitHub API response: { e } " )
121+ sys .exit (1 )
122+
123+ def main ():
124+ """Main function."""
125+ # Get inputs
126+ artifact_name = os .getenv ('INPUT_ARTIFACT_NAME' )
127+ if not artifact_name :
128+ print ("::error::artifact_name input is required" )
129+ sys .exit (1 )
130+
131+ # Get environment variables
132+ token = get_github_token ()
133+ repo = get_repository ()
134+ head_sha = get_head_sha ()
135+
136+ print (f"::debug::Checking for artifact '{ artifact_name } ' in repository '{ repo } ', head_sha '{ head_sha } '" )
137+
138+ # Check if artifact exists
139+ exists = check_artifact_exists (artifact_name , token , repo , head_sha )
140+
141+ # Set outputs
142+ set_output ('exists' , str (exists ).lower ())
143+
144+ print (f"::debug::Artifact { artifact_name } exists: { exists } " )
145+
146+ if __name__ == '__main__' :
147+ main ()
0 commit comments