1+ import { existsSync , readFileSync } from 'node:fs' ;
12import process from 'node:process' ;
23
34import * as core from '@actions/core' ;
@@ -17,6 +18,7 @@ import {
1718} from './versioning' ;
1819import { createOrUpdatePullRequest , findExistingPullRequest } from './git' ;
1920import { validateTrack } from './config' ;
21+ import type { StableTag } from './github' ;
2022
2123const DEFAULT_TRACK = '3.13' ;
2224const DEFAULT_PATHS = [
@@ -51,6 +53,48 @@ function resolvePathsInput(): string[] {
5153 return explicitPaths . length > 0 ? explicitPaths : DEFAULT_PATHS ;
5254}
5355
56+ function loadJsonSnapshot ( envName : string ) : unknown | undefined {
57+ const raw = process . env [ envName ] ;
58+ if ( ! raw || raw . trim ( ) === '' ) {
59+ return undefined ;
60+ }
61+
62+ const trimmed = raw . trim ( ) ;
63+
64+ try {
65+ return JSON . parse ( trimmed ) ;
66+ } catch ( primaryError ) {
67+ if ( existsSync ( trimmed ) ) {
68+ try {
69+ const fileContent = readFileSync ( trimmed , 'utf8' ) ;
70+ return JSON . parse ( fileContent ) ;
71+ } catch ( fileError ) {
72+ throw new Error (
73+ `Failed to parse JSON snapshot from ${ envName } . Ensure it contains valid JSON or a path to a JSON file. Original error: ${ ( fileError as Error ) . message } ` ,
74+ ) ;
75+ }
76+ }
77+
78+ throw new Error (
79+ `Failed to parse JSON snapshot from ${ envName } . Provide valid JSON or a path to a JSON file. Original error: ${ ( primaryError as Error ) . message } ` ,
80+ ) ;
81+ }
82+ }
83+
84+ function loadTextSnapshot ( envName : string ) : string | undefined {
85+ const raw = process . env [ envName ] ;
86+ if ( ! raw || raw . trim ( ) === '' ) {
87+ return undefined ;
88+ }
89+
90+ const trimmed = raw . trim ( ) ;
91+ if ( existsSync ( trimmed ) ) {
92+ return readFileSync ( trimmed , 'utf8' ) ;
93+ }
94+
95+ return raw ;
96+ }
97+
5498function parseRepository ( slug : string | undefined ) : { owner : string ; repo : string } | null {
5599 if ( ! slug ) {
56100 return null ;
@@ -165,6 +209,31 @@ export async function run(): Promise<void> {
165209 const repository = parseRepository ( process . env . GITHUB_REPOSITORY ) ;
166210 const githubToken = process . env . GITHUB_TOKEN ;
167211 const defaultBranch = process . env . GITHUB_BASE_REF ?? process . env . GITHUB_REF_NAME ?? 'main' ;
212+ const noNetworkFallback = ( process . env . NO_NETWORK_FALLBACK ?? '' ) . toLowerCase ( ) === 'true' ;
213+
214+ let cpythonTagsSnapshot : StableTag [ ] | undefined ;
215+ let pythonOrgHtmlSnapshot : string | undefined ;
216+ let runnerManifestSnapshot : unknown | undefined ;
217+
218+ try {
219+ const rawTagsSnapshot = loadJsonSnapshot ( 'CPYTHON_TAGS_SNAPSHOT' ) ;
220+ if ( rawTagsSnapshot !== undefined ) {
221+ if ( ! Array . isArray ( rawTagsSnapshot ) ) {
222+ throw new Error ( 'CPYTHON_TAGS_SNAPSHOT must be a JSON array.' ) ;
223+ }
224+ cpythonTagsSnapshot = rawTagsSnapshot as StableTag [ ] ;
225+ }
226+
227+ pythonOrgHtmlSnapshot = loadTextSnapshot ( 'PYTHON_ORG_HTML_SNAPSHOT' ) ;
228+ runnerManifestSnapshot = loadJsonSnapshot ( 'RUNNER_MANIFEST_SNAPSHOT' ) ;
229+ } catch ( snapshotError ) {
230+ if ( snapshotError instanceof Error ) {
231+ core . setFailed ( snapshotError . message ) ;
232+ } else {
233+ core . setFailed ( 'Failed to load offline snapshots.' ) ;
234+ }
235+ return ;
236+ }
168237
169238 core . startGroup ( 'Configuration' ) ;
170239 core . info ( `workspace: ${ workspace } ` ) ;
@@ -173,6 +242,7 @@ export async function run(): Promise<void> {
173242 core . info ( `paths (${ effectivePaths . length } ): ${ effectivePaths . join ( ', ' ) } ` ) ;
174243 core . info ( `automerge: ${ automerge } ` ) ;
175244 core . info ( `dry_run: ${ dryRun } ` ) ;
245+ core . info ( `no_network_fallback: ${ noNetworkFallback } ` ) ;
176246 if ( repository ) {
177247 core . info ( `repository: ${ repository . owner } /${ repository . repo } ` ) ;
178248 }
@@ -196,6 +266,12 @@ export async function run(): Promise<void> {
196266 repository,
197267 defaultBranch,
198268 allowPrCreation : false ,
269+ noNetworkFallback,
270+ snapshots : {
271+ cpythonTags : cpythonTagsSnapshot ,
272+ pythonOrgHtml : pythonOrgHtmlSnapshot ,
273+ runnerManifest : runnerManifestSnapshot ,
274+ } ,
199275 } ,
200276 dependencies ,
201277 ) ;
0 commit comments