diff --git a/index.js b/index.js new file mode 100755 index 0000000..53cd201 --- /dev/null +++ b/index.js @@ -0,0 +1,145 @@ +#!/usr/bin/env node + +const https = require("https"); +const fs = require("fs"); +const path = require("path"); +const os = require("os"); +const { URL } = require("url"); +const yaml = require("yaml"); + +// CLI flags +const args = process.argv.slice(2); +const asJSON = args.includes("--json"); +const asMarkdown = args.includes("--markdown"); + +// === STEP 1: Load config from tea YAML === +function findTeaConfig() { + const xdg = process.env.XDG_CONFIG_HOME; + const home = os.homedir(); + const platform = process.platform; + + const paths = []; + + // 1. XDG_CONFIG_HOME override + if (xdg) { + paths.push(path.join(xdg, "tea", "config.yml")); + } + + // 2. Platform-specific default + if (platform === "darwin") { + // macOS: ~/Library/Application Support/tea/config.yml + paths.push(path.join(home, "Library", "Application Support", "tea", "config.yml")); + } else { + // Linux/others: ~/.config/tea/config.yml + paths.push(path.join(home, ".config", "tea", "config.yml")); + } + + // 3. Legacy fallback + paths.push(path.join(home, ".tea", "tea.yml")); + + // 4. First existing config wins + for (const p of paths) { + if (fs.existsSync(p)) return p; + } + + return null; +} + +function loadGiteaLogin() { + const configPath = findTeaConfig(); + if (!configPath) { + console.error("❌ Could not find tea config.yml or tea.yml"); + process.exit(1); + } + + const file = fs.readFileSync(configPath, "utf8"); + const parsed = yaml.parse(file); + const logins = parsed.logins || []; + + if (logins.length === 0) { + console.error("❌ No logins found in config"); + process.exit(1); + } + + const login = logins.find((l) => l.default) || logins[0]; + return { + baseUrl: login.url.replace(/\/$/, ""), + token: login.token, + user: login.user, + }; +} + +// === STEP 2: Fetch package list from Gitea === +function fetchJSON(baseUrl, path, token) { + return new Promise((resolve, reject) => { + const url = new URL(path, baseUrl); + const options = { + headers: { + Authorization: `token ${token}`, + Accept: "application/json", + }, + }; + + https.get(url, options, (res) => { + let body = ""; + res.on("data", (chunk) => (body += chunk)); + res.on("end", () => { + try { + const json = JSON.parse(body); + resolve(json); + } catch (err) { + reject(new Error("Failed to parse JSON: " + err.message)); + } + }); + }).on("error", reject); + }); +} + +// === STEP 3: Output functions === +function printPlain(packages) { + packages.forEach(pkg => { + console.log(`${pkg.name}@${pkg.version} — ${pkg.created_at}`); + }); +} + +function printMarkdown(packages) { + console.log(`| Package | Version | Created At |`); + console.log(`|---------|---------|------------|`); + packages.forEach(pkg => { + const name = `\`${pkg.name}\``; + const version = `\`${pkg.version}\``; + const created = new Date(pkg.created_at).toISOString().split("T")[0]; + console.log(`| ${name} | ${version} | ${created} |`); + }); +} + +function printJSON(packages) { + console.log(JSON.stringify(packages, null, 2)); +} + +// === STEP 4: Main logic === +async function listPackages() { + const { baseUrl, token, user } = loadGiteaLogin(); + + const isOrg = false; // You can change this or extend to CLI option + const endpoint = isOrg + ? `/api/v1/orgs/${user}/packages/npm` + : `/api/v1/users/${user}/packages/npm`; + + try { + const packages = await fetchJSON(baseUrl, endpoint, token); + if (!Array.isArray(packages)) throw new Error("Unexpected response"); + + if (asJSON) { + printJSON(packages); + } else if (asMarkdown) { + printMarkdown(packages); + } else { + printPlain(packages); + } + } catch (err) { + console.error("❌ Error:", err.message); + } +} + +listPackages(); diff --git a/package.json b/package.json new file mode 100644 index 0000000..da48ca6 --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "gitea-npm", + "version": "1.0.0", + "description": "Manage npm packages stored on gitea", + "main": "index.js", + "bin": "npm-gitea", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "npm", + "git", + "gitea" + ], + "author": "William Ross ", + "license": "ISC", + "dependencies": { + "yaml": "^2.7.1" + } +}