From e64055886eaedd17091653541c23511ad367e196 Mon Sep 17 00:00:00 2001 From: fredwes <6827305+fredwes@users.noreply.github.com> Date: Mon, 6 Apr 2026 11:23:53 -0400 Subject: [PATCH] cd: add missing action files --- .github/actions/action.yml | 26 --- .github/actions/deplopy-ipfs/action.yml | 26 --- .github/actions/deploy-ipfs/index.js | 210 +++++++++++++++++++++++ .github/actions/deploy-ipfs/package.json | 11 ++ 4 files changed, 221 insertions(+), 52 deletions(-) delete mode 100644 .github/actions/action.yml delete mode 100644 .github/actions/deplopy-ipfs/action.yml create mode 100644 .github/actions/deploy-ipfs/index.js create mode 100644 .github/actions/deploy-ipfs/package.json diff --git a/.github/actions/action.yml b/.github/actions/action.yml deleted file mode 100644 index 21da1852..00000000 --- a/.github/actions/action.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: 'Deploy to IPFS via Pinata' -description: 'Upload directory contents to IPFS using Pinata' -inputs: - pinata-jwt: - description: 'Pinata JWT token' - required: true - source-dir: - description: 'Directory to upload to IPFS' - required: true - default: './dist' - pin-name: - description: 'Name for the pinned content' - required: false - default: 'GitHub Actions Deploy' - update-existing: - description: 'Whether to update existing pin with same name' - required: false - default: 'true' -outputs: - ipfs-hash: - description: 'The IPFS hash of the uploaded content' - gateway-url: - description: 'The gateway URL to access the content' -runs: - using: 'node20' - main: 'index.js' diff --git a/.github/actions/deplopy-ipfs/action.yml b/.github/actions/deplopy-ipfs/action.yml deleted file mode 100644 index 21da1852..00000000 --- a/.github/actions/deplopy-ipfs/action.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: 'Deploy to IPFS via Pinata' -description: 'Upload directory contents to IPFS using Pinata' -inputs: - pinata-jwt: - description: 'Pinata JWT token' - required: true - source-dir: - description: 'Directory to upload to IPFS' - required: true - default: './dist' - pin-name: - description: 'Name for the pinned content' - required: false - default: 'GitHub Actions Deploy' - update-existing: - description: 'Whether to update existing pin with same name' - required: false - default: 'true' -outputs: - ipfs-hash: - description: 'The IPFS hash of the uploaded content' - gateway-url: - description: 'The gateway URL to access the content' -runs: - using: 'node20' - main: 'index.js' diff --git a/.github/actions/deploy-ipfs/index.js b/.github/actions/deploy-ipfs/index.js new file mode 100644 index 00000000..0ae02db2 --- /dev/null +++ b/.github/actions/deploy-ipfs/index.js @@ -0,0 +1,210 @@ +const core = require('@actions/core'); +const fs = require('fs'); +const path = require('path'); +const { PinataSDK } = require('pinata'); + +async function run() { + try { + // Get inputs + const pinataJwt = core.getInput('pinata-jwt'); + const sourceDir = core.getInput('source-dir'); + const pinName = core.getInput('pin-name'); + const updateExisting = core.getInput('update-existing') === 'true'; + + // Initialize Pinata SDK v2 + const pinata = new PinataSDK({ + pinataJwt: pinataJwt + }); + + // Test authentication + try { + await pinata.testAuthentication(); + console.log('✅ Pinata authentication successful'); + } catch (error) { + throw new Error(`Authentication failed: ${error.message}`); + } + + // Check if source directory exists + if (!fs.existsSync(sourceDir)) { + throw new Error(`Source directory ${sourceDir} does not exist`); + } + + // If updating existing, try to find and remove old version + if (updateExisting) { + try { + const existingFiles = await pinata.files.public.list() + .name(pinName) + .limit(1); + + if (existingFiles.files && existingFiles.files.length > 0) { + const oldFile = existingFiles.files[0]; + console.log(`🗑️ Removing old version: ${oldFile.cid}`); + await pinata.files.public.delete([oldFile.id]); + } + } catch (error) { + console.log('⚠️ Could not remove old version:', error.message); + } + } + + // Create file array by reading all files + const files = await getAllFiles(sourceDir); + console.log(`📁 Found ${files.length} files to upload`); + + // Upload directory to IPFS using v2 SDK + console.log('🚀 Uploading to IPFS...'); + + const result = await pinata.upload.public.fileArray(files) + .name(pinName) + .keyvalues({ + 'deployment': 'github-actions', + 'repository': process.env.GITHUB_REPOSITORY || 'unknown', + 'commit': process.env.GITHUB_SHA || 'unknown', + 'branch': process.env.GITHUB_REF_NAME || 'unknown', + 'timestamp': new Date().toISOString() + }); + + console.log('✅ Upload successful!'); + console.log(`📍 IPFS Hash: ${result.cid}`); + console.log(`🌍 Gateway URL: https://gateway.pinata.cloud/ipfs/${result.cid}`); + + // Set outputs for GitHub Actions + core.setOutput('ipfs-hash', result.cid); + core.setOutput('gateway-url', `https://gateway.pinata.cloud/ipfs/${result.cid}`); + core.setOutput('pin-id', result.id); + core.setOutput('pin-name', result.name); + + // Create a detailed summary + await core.summary + .addHeading('🎉 IPFS Deployment Successful!') + .addTable([ + ['Property', 'Value'], + ['IPFS Hash (CID)', result.cid], + ['Gateway URL', `https://gateway.pinata.cloud/ipfs/${result.cid}`], + ['Pin Name', result.name || pinName], + ['Pin ID', result.id], + ['Files Uploaded', files.length.toString()], + ['Upload Size', formatBytes(result.size || 0)], + ['Upload Date', new Date(result.created_at).toLocaleString('en-US')], + ['Repository', process.env.GITHUB_REPOSITORY || 'N/A'], + ['Commit', process.env.GITHUB_SHA ? process.env.GITHUB_SHA.substring(0, 8) : 'N/A'] + ]) + .addDetails( + 'Upload Details', + ` +**Network:** ${result.network || 'public'} +**MIME Type:** ${result.mime_type || 'N/A'} +**Number of Files:** ${result.number_of_files || files.length} +**Vectorized:** ${result.vectorized ? 'Yes' : 'No'} + +**Access your content at:** https://gateway.pinata.cloud/ipfs/${result.cid} + `.trim() + ) + .write(); + + console.log('📋 Summary created successfully'); + + } catch (error) { + console.error('❌ Deployment failed:', error.message); + core.setFailed(`Action failed: ${error.message}`); + } +} + +async function getAllFiles(dirPath, arrayOfFiles = [], basePath = '') { + const files = fs.readdirSync(dirPath); + + for (const file of files) { + const fullPath = path.join(dirPath, file); + const stat = fs.statSync(fullPath); + + if (stat.isDirectory()) { + // Recursively process subdirectories + const subPath = basePath ? path.join(basePath, file) : file; + arrayOfFiles = await getAllFiles(fullPath, arrayOfFiles, subPath); + } else { + try { + // Read file content + const fileContent = fs.readFileSync(fullPath); + + // Create relative path for the file + const relativePath = basePath ? path.join(basePath, file) : file; + + // Create File object with proper path and MIME type + const fileObj = new File([fileContent], relativePath, { + type: getMimeType(file) + }); + + arrayOfFiles.push(fileObj); + console.log(`📄 Added file: ${relativePath} (${formatBytes(stat.size)})`); + + } catch (error) { + console.warn(`⚠️ Could not read file ${fullPath}: ${error.message}`); + } + } + } + + return arrayOfFiles; +} + +function getMimeType(filename) { + const ext = path.extname(filename).toLowerCase(); + const mimeTypes = { + // Web files + '.html': 'text/html', + '.htm': 'text/html', + '.css': 'text/css', + '.js': 'application/javascript', + '.mjs': 'application/javascript', + '.json': 'application/json', + '.xml': 'application/xml', + + // Images + '.png': 'image/png', + '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.gif': 'image/gif', + '.webp': 'image/webp', + '.svg': 'image/svg+xml', + '.ico': 'image/x-icon', + + // Documents + '.pdf': 'application/pdf', + '.txt': 'text/plain', + '.md': 'text/markdown', + '.rtf': 'application/rtf', + + // Fonts + '.woff': 'font/woff', + '.woff2': 'font/woff2', + '.ttf': 'font/ttf', + '.eot': 'application/vnd.ms-fontobject', + + // Archives + '.zip': 'application/zip', + '.tar': 'application/x-tar', + '.gz': 'application/gzip', + + // Media + '.mp4': 'video/mp4', + '.webm': 'video/webm', + '.mp3': 'audio/mpeg', + '.wav': 'audio/wav', + '.ogg': 'audio/ogg' + }; + + return mimeTypes[ext] || 'application/octet-stream'; +} + +function formatBytes(bytes, decimals = 2) { + if (!bytes || bytes === 0) return '0 Bytes'; + + const k = 1024; + const dm = decimals < 0 ? 0 : decimals; + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB']; + + const i = Math.floor(Math.log(bytes) / Math.log(k)); + + return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; +} + +// Run the main function +run(); \ No newline at end of file diff --git a/.github/actions/deploy-ipfs/package.json b/.github/actions/deploy-ipfs/package.json new file mode 100644 index 00000000..d0331ed2 --- /dev/null +++ b/.github/actions/deploy-ipfs/package.json @@ -0,0 +1,11 @@ +{ + "name": "deploy-ipfs-action", + "version": "1.0.0", + "description": "GitHub Action to deploy to IPFS via Pinata", + "main": "index.js", + "dependencies": { + "@actions/core": "^1.10.1", + "pinata": "^2.1.0" + } +} + \ No newline at end of file