1+ name : Create README Gist
2+
3+ on :
4+ workflow_dispatch :
5+ inputs :
6+ repository :
7+ description : " Repo name in the serpgames org, or serpgames/repo"
8+ required : true
9+ type : string
10+ ref :
11+ description : " Branch, tag, or SHA to read from. Leave blank to use the repo default branch."
12+ required : false
13+ default : " "
14+ type : string
15+ public :
16+ description : " Create a public gist"
17+ required : false
18+ default : true
19+ type : boolean
20+ gist_description :
21+ description : " Optional gist description"
22+ required : false
23+ default : " "
24+ type : string
25+ workflow_call :
26+ inputs :
27+ repository :
28+ description : " Repo name in the serpgames org, or serpgames/repo"
29+ required : true
30+ type : string
31+ ref :
32+ description : " Branch, tag, or SHA to read from. Leave blank to use the repo default branch."
33+ required : false
34+ default : " "
35+ type : string
36+ public :
37+ description : " Create a public gist"
38+ required : false
39+ default : true
40+ type : boolean
41+ gist_description :
42+ description : " Optional gist description"
43+ required : false
44+ default : " "
45+ type : string
46+ secrets :
47+ SERPGAMEDEVELOPER_GIST_TOKEN :
48+ description : " PAT from the serpgamedeveloper account with gist scope"
49+ required : true
50+ outputs :
51+ gist_url :
52+ description : " Created gist URL"
53+ value : ${{ jobs.create-readme-gist.outputs.gist_url }}
54+ gist_id :
55+ description : " Created gist ID"
56+ value : ${{ jobs.create-readme-gist.outputs.gist_id }}
57+
58+ permissions :
59+ contents : read
60+
61+ jobs :
62+ create-readme-gist :
63+ name : Create README gist
64+ runs-on : ubuntu-latest
65+ outputs :
66+ gist_url : ${{ steps.create.outputs.gist_url }}
67+ gist_id : ${{ steps.create.outputs.gist_id }}
68+ steps :
69+ - name : Create gist from repository README
70+ id : create
71+ shell : bash
72+ env :
73+ INPUT_REPOSITORY : ${{ inputs.repository }}
74+ INPUT_REF : ${{ inputs.ref }}
75+ GIST_PUBLIC : ${{ inputs.public }}
76+ GIST_DESCRIPTION : ${{ inputs.gist_description }}
77+ GITHUB_TOKEN : ${{ github.token }}
78+ SERPGAMEDEVELOPER_GIST_TOKEN : ${{ secrets.SERPGAMEDEVELOPER_GIST_TOKEN }}
79+ run : |
80+ node <<'NODE'
81+ const fs = require("fs");
82+
83+ const apiVersion = "2022-11-28";
84+ const readToken = process.env.GITHUB_TOKEN || "";
85+ const gistToken = process.env.SERPGAMEDEVELOPER_GIST_TOKEN || "";
86+
87+ if (!gistToken) {
88+ throw new Error("Missing SERPGAMEDEVELOPER_GIST_TOKEN secret. Add a serpgamedeveloper PAT with gist scope.");
89+ }
90+
91+ const rawRepository = (process.env.INPUT_REPOSITORY || "").trim();
92+ if (!rawRepository) {
93+ throw new Error("The repository input is required.");
94+ }
95+
96+ const parts = rawRepository.includes("/")
97+ ? rawRepository.split("/")
98+ : ["serpgames", rawRepository];
99+
100+ if (parts.length !== 2 || parts[0] !== "serpgames") {
101+ throw new Error("repository must be a repo name in serpgames, or serpgames/repo-name.");
102+ }
103+
104+ const [owner, repo] = parts;
105+ if (!/^[A-Za-z0-9_.-]+$/.test(repo)) {
106+ throw new Error(`Invalid repository name: ${repo}`);
107+ }
108+
109+ const githubHeaders = {
110+ "Accept": "application/vnd.github+json",
111+ "X-GitHub-Api-Version": apiVersion,
112+ "User-Agent": "serpgames-readme-gist-action",
113+ };
114+ if (readToken) {
115+ githubHeaders.Authorization = `Bearer ${readToken}`;
116+ }
117+
118+ async function requestJson(url, options = {}) {
119+ const response = await fetch(url, {
120+ ...options,
121+ headers: {
122+ ...githubHeaders,
123+ ...(options.headers || {}),
124+ },
125+ });
126+ const text = await response.text();
127+ let body = null;
128+ if (text) {
129+ try {
130+ body = JSON.parse(text);
131+ } catch {
132+ body = text;
133+ }
134+ }
135+ if (!response.ok) {
136+ const details = typeof body === "string" ? body : JSON.stringify(body);
137+ throw new Error(`GitHub API request failed: ${response.status} ${response.statusText} ${url} ${details}`);
138+ }
139+ return body;
140+ }
141+
142+ const repoInfo = await requestJson(`https://api.github.com/repos/${owner}/${repo}`);
143+ const requestedRef = (process.env.INPUT_REF || "").trim();
144+ const ref = requestedRef || repoInfo.default_branch;
145+ const readme = await requestJson(
146+ `https://api.github.com/repos/${owner}/${repo}/readme?ref=${encodeURIComponent(ref)}`
147+ );
148+
149+ if (readme.encoding !== "base64") {
150+ throw new Error(`Unsupported README encoding: ${readme.encoding}`);
151+ }
152+
153+ const readmeMarkdown = Buffer.from(readme.content.replace(/\n/g, ""), "base64").toString("utf8");
154+ const gistPublic = String(process.env.GIST_PUBLIC || "true").toLowerCase() !== "false";
155+ const generatedAt = new Date().toISOString();
156+ const description = (process.env.GIST_DESCRIPTION || "").trim()
157+ || `${owner}/${repo} README from ${ref}`;
158+
159+ const metadata = {
160+ repository: `${owner}/${repo}`,
161+ ref,
162+ default_branch: repoInfo.default_branch,
163+ readme_path: readme.path,
164+ readme_sha: readme.sha,
165+ readme_size: readme.size,
166+ readme_html_url: readme.html_url,
167+ readme_download_url: readme.download_url,
168+ generated_at: generatedAt,
169+ };
170+
171+ const gist = await requestJson("https://api.github.com/gists", {
172+ method: "POST",
173+ headers: {
174+ Authorization: `Bearer ${gistToken}`,
175+ },
176+ body: JSON.stringify({
177+ description,
178+ public: gistPublic,
179+ files: {
180+ [`${repo}-README.md`]: {
181+ content: readmeMarkdown,
182+ },
183+ [`${repo}-readme-metadata.json`]: {
184+ content: JSON.stringify(metadata, null, 2),
185+ },
186+ },
187+ }),
188+ });
189+
190+ fs.appendFileSync(process.env.GITHUB_OUTPUT, `gist_url=${gist.html_url}\n`);
191+ fs.appendFileSync(process.env.GITHUB_OUTPUT, `gist_id=${gist.id}\n`);
192+
193+ if (process.env.GITHUB_STEP_SUMMARY) {
194+ fs.appendFileSync(
195+ process.env.GITHUB_STEP_SUMMARY,
196+ [
197+ "## README gist created",
198+ "",
199+ `- Repository: ${owner}/${repo}`,
200+ `- Ref: ${ref}`,
201+ `- README: ${readme.html_url}`,
202+ `- Gist: ${gist.html_url}`,
203+ `- Public: ${gist.public}`,
204+ "",
205+ ].join("\n")
206+ );
207+ }
208+ NODE
0 commit comments