Upload Image to GitHub Pages
Upload an image file to GitHub Pages and return a public URL. Uses a shared media-assets repo with date-based organization.
Source Code
import fs from "fs";
import path from "path";
const REPO_NAME = "media-assets";
const [imagePath, customFilename = "", trackingPath] = process.argv.slice(2);
// Validate inputs
if (!imagePath) {
console.error("Error: imagePath is required");
process.exit(1);
}
if (!fs.existsSync(imagePath)) {
console.error(`Error: File not found: ${imagePath}`);
process.exit(1);
}
// Get file info
const originalExt = path.extname(imagePath).toLowerCase();
const originalName = path.basename(imagePath, originalExt);
const supportedExts = [".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"];
if (!supportedExts.includes(originalExt)) {
console.error(
`Error: Unsupported file type: ${originalExt}. Supported: ${supportedExts.join(", ")}`
);
process.exit(1);
}
// Generate filename
const timestamp = Date.now();
const sanitizedCustomName = customFilename
? customFilename
.toLowerCase()
.replace(/[^a-z0-9-]/g, "-")
.replace(/-+/g, "-")
.replace(/^-|-$/g, "")
: "";
const finalFilename = sanitizedCustomName
? `${sanitizedCustomName}${originalExt}`
: `${originalName}-${timestamp}${originalExt}`;
// Date-based path: YYYY/MM/DD/filename
const now = new Date();
const datePath = `${now.getFullYear()}/${String(now.getMonth() + 1).padStart(2, "0")}/${String(now.getDate()).padStart(2, "0")}`;
const remotePath = `${datePath}/${finalFilename}`;
// Read file and convert to base64
const fileBuffer = fs.readFileSync(imagePath);
const contentBase64 = fileBuffer.toString("base64");
const fileSize = fileBuffer.length;
console.log(`Uploading image to GitHub Pages...`);
console.log(` Source: ${imagePath}`);
console.log(` Size: ${fileSize} bytes`);
console.log(` Remote path: ${remotePath}`);
// GitHub API helper
async function githubFetch(url, options = {}) {
const response = await fetch(url, {
...options,
headers: {
Authorization: "Bearer PLACEHOLDER_TOKEN",
Accept: "application/vnd.github.v3+json",
"User-Agent": "Sauna-Agent",
...options.headers,
},
});
return response;
}
try {
// Step 1: Get authenticated user
const userResponse = await githubFetch("https://api.github.com/user");
if (!userResponse.ok) {
const error = await userResponse.text();
console.error("Failed to authenticate with GitHub");
console.error(error);
throw new Error("GitHub authentication failed");
}
const user = await userResponse.json();
const username = user.login;
console.log(` Authenticated as: ${username}`);
// Step 2: Check if media-assets repo exists
const repoCheckResponse = await githubFetch(
`https://api.github.com/repos/${username}/${REPO_NAME}`
);
let repoExists = repoCheckResponse.ok;
// Step 3: Create repo if it doesn't exist
if (!repoExists) {
console.log(` Repository ${REPO_NAME} does not exist. Creating...`);
const createRepoResponse = await githubFetch(
"https://api.github.com/user/repos",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
name: REPO_NAME,
description: "Hosted media assets for Sauna skills",
homepage: `https://${username}.github.io/${REPO_NAME}`,
private: false,
auto_init: true,
has_pages: true,
}),
}
);
if (!createRepoResponse.ok) {
const error = await createRepoResponse.text();
console.error("Failed to create repository");
console.error(error);
throw new Error("Repository creation failed");
}
console.log(" Repository created successfully");
// Wait for repo initialization
await new Promise((resolve) => setTimeout(resolve, 2000));
// Enable GitHub Pages
console.log(" Enabling GitHub Pages...");
try {
await githubFetch(
`https://api.github.com/repos/${username}/${REPO_NAME}/pages`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
source: {
branch: "main",
path: "/",
},
}),
}
);
console.log(" GitHub Pages enabled");
} catch {
console.log(" Note: GitHub Pages may need manual activation");
}
} else {
console.log(` Repository ${REPO_NAME} exists`);
}
// Step 4: Check for existing file to get SHA (for updates)
const getFileResponse = await githubFetch(
`https://api.github.com/repos/${username}/${REPO_NAME}/contents/${remotePath}`
);
let sha = null;
if (getFileResponse.ok) {
const fileData = await getFileResponse.json();
sha = fileData.sha;
console.log(" Updating existing file");
} else {
console.log(" Creating new file");
}
// Step 5: Upload/update file
const uploadBody = {
message: `Upload ${finalFilename}`,
content: contentBase64,
};
if (sha) {
uploadBody.sha = sha;
}
const uploadResponse = await githubFetch(
`https://api.github.com/repos/${username}/${REPO_NAME}/contents/${remotePath}`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(uploadBody),
}
);
if (!uploadResponse.ok) {
const error = await uploadResponse.text();
console.error("Failed to upload file");
console.error(error);
throw new Error("File upload failed");
}
const result = await uploadResponse.json();
// Output results
const imageUrl = `https://${username}.github.io/${REPO_NAME}/${remotePath}`;
const repoUrl = `https://github.com/${username}/${REPO_NAME}`;
console.log("\nโ
Uploaded successfully!");
console.log(`\n๐ Image URL: ${imageUrl}`);
console.log(`๐ Repository: ${repoUrl}`);
console.log(`๐ Commit: ${result.commit.sha.substring(0, 7)}`);
console.log(
"\nNote: GitHub Pages may take 1-2 minutes to serve new images."
);
// Output structured result for downstream use
const output = {
success: true,
url: imageUrl,
repository: repoUrl,
filename: finalFilename,
remotePath: remotePath,
username: username,
commit: result.commit.sha,
size: fileSize,
timestamp: new Date().toISOString(),
};
console.log("\n--- RESULT ---");
console.log(JSON.stringify(output, null, 2));
// Step 6: Update tracking file if provided
if (trackingPath) {
let images = [];
if (fs.existsSync(trackingPath)) {
images = JSON.parse(fs.readFileSync(trackingPath, "utf-8"));
}
images.push({
url: imageUrl,
filename: finalFilename,
remotePath: remotePath,
size: fileSize,
uploadedAt: output.timestamp,
});
fs.writeFileSync(trackingPath, JSON.stringify(images, null, 2));
console.log(`\n๐ Updated tracking: ${trackingPath}`);
}
} catch (error) {
console.error(`\nโ Upload failed: ${error.message}`);
process.exit(1);
}