@ycmd/creds
Version:
LSK.js CLI Creds is the easiest way to manage GitHub / Gitlab secrets and credentials
20 lines (18 loc) • 10.8 kB
JavaScript
import { relative } from 'node:path';
import { getComment, jsonToFile } from '@lsk4/stringify';
import { map, mapSeries } from 'fishbird';
import { unlink, mkdir, readdir } from 'fs/promises';
import { createLogger } from '@lsk4/log';
import { Err } from '@lsk4/err';
import K from 'axios';
import U from 'libsodium-wrappers';
import { existsSync } from 'node:fs';
import { resolve } from 'path';
import { readFile } from 'node:fs/promises';
var F=Object.defineProperty;var c=(i,t)=>F(i,"name",{value:t,configurable:!0}),b=(i=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(i,{get:(t,e)=>(typeof require<"u"?require:t)[e]}):i)(function(i){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+i+'" is not supported')});var w=createLogger("creds");var y=class{static{c(this,"Service");}client;log=createLogger(this.constructor.name);constructor(t){this.assign(t),this.checkConfig(),this.client=this.createClient(t.clientOptions);}createClient(t={}){return K.create(t)}assign(t){Object.assign(this,t);}checkConfig(){throw new Err("NOT_IMPLEMENTED","checkConfig method not implemented")}getServiceHostname(){throw new Err("NOT_IMPLEMENTED","getServiceHostname method not implemented")}getProjectUrl(){throw new Err("NOT_IMPLEMENTED","getProjectUrl method not implemented")}getProjectCICDSettingURL(){throw new Err("NOT_IMPLEMENTED","getProjectCICDSettingURL method not implemented")}getProjectPath(){let t=this.projectPath;if(!t)throw new Err("!projectPath");return t}getProjectId(){return this.projectId}getProjectCredsUrl(){let t=this.projectCredsUrl;if(!t)throw new Err("!projectCredsUrl");return t}getProjectCredsOwner(){let t=this.projectCredsOwner;if(!t)throw new Err("!projectCredsOwner");return t}async uploadSecret(t,e){throw new Err("NOT_IMPLEMENTED","uploadSecret method not implemented")}async uploadVariable(t,e){throw new Err("NOT_IMPLEMENTED","uploadVariable method not implemented")}async removeOldHooks(){}async uploadHook(t){}async uploadHooks(t){if(!t)throw new Err("!env");let{hooks:e=[]}=t;try{await this.removeOldHooks();}catch(o){this.log.error("[ERR] Old hooks removing failed:",Err.getMessage(o));}await map(e,async(o,r)=>{try{await this.uploadHook(o),this.log.info(`[OK] Hook ${r} uploaded`);}catch(a){this.log.error(`[ERR] Hook ${r} not uploaded:`,Err.getMessage(a));}});}async uploadAll(t){if(!t)throw new Err("!env");let{secrets:e={},variables:o={},files:r=[]}=t;await this.uploadHooks(t),await map(Object.entries(e),async([a,s])=>{try{await this.uploadSecret(a,s),this.log.info(`[OK] Secret ${a} uploaded`);}catch(n){this.log.error(`[ERR] Secret ${a} not uploaded as secret, because`,Err.getMessage(n)),this.log.trace(n);}}),await map(Object.entries(o),async([a,s])=>{try{await this.uploadVariable(a,s),this.log.info(`[OK] Variable ${a} uploaded`);}catch(n){this.log.error(`[ERR] Variable ${a} not uploaded as variable, because`,Err.getMessage(n)),this.log.trace(n);}}),await map(Object.values(r),async({name:a,credType:s,content:n})=>{let h=a,f=n;try{if(s==="variable")await this.uploadVariable(h,f);else if(s==="secret")await this.uploadSecret(h,f);else if(s==="skip"){this.log.debug(`[SKIP] File ${h} uploaded as ${s}`);return}else throw new Err("unknown credType",{credType:s});this.log.info(`[OK] File ${h} uploaded as ${s}`);}catch(d){this.log.error(`[ERR] File ${h} not uploaded as ${s}, because`,Err.getMessage(d)),this.log.trace(d);}});}};var k=class extends y{static{c(this,"GithubService");}projectName;projectPath;projectCredsUrl;projectCredsOwner;token;server;log=createLogger(this.constructor.name);constructor(t){super(t),this.assign(t),this.checkConfig(),this.client=this.createClient(t.clientOptions);}checkConfig(){if(!this.projectName)throw new Err("!projectName");if(!this.projectPath)throw new Err("!projectPath");if(!this.projectCredsUrl)throw new Err("!projectCredsUrl");if(!this.projectCredsOwner)throw new Err("!projectCredsOwner");if(!this.token)throw new Err("!token")}createClient(t={}){let o=`https://${this.server||"api.github.com"}/repos/${this.getProjectPath()}`;return K.create({baseURL:o,headers:{Accept:"application/vnd.github+json",Authorization:`Bearer ${this.token}`,"X-GitHub-Api-Version":"2022-11-28"},...t})}getServiceHostname(){return "github.com"}getProjectPath(){return this.projectPath}getProjectUrl(){return `https://${this.getServiceHostname()}/${this.getProjectPath()}`}getProjectCICDSettingURL(){return `${this.getProjectUrl()}/settings/secrets/actions`}async uploadSecret(t,e){let{data:o}=await this.client({method:"get",url:"/actions/secrets/public-key"}).catch(d=>{throw new Err(d.message,{data:d?.response?.data})}),r=typeof e=="string"?e:e.value;if(!o?.key)throw new Err("!publicKey");if(!o?.key_id)throw new Err("!publicKeyId");await U.ready;let a=U,s=a.from_base64(o.key,a.base64_variants.ORIGINAL),n=a.from_string(r),h=a.crypto_box_seal(n,s),f=a.to_base64(h,a.base64_variants.ORIGINAL);await this.client({method:"put",url:`/actions/secrets/${t}`,data:{encrypted_value:f,key_id:o.key_id}});}async uploadVariable(t,e){let o=typeof e=="string"?e:e.value,{data:r,status:a}=await this.client({method:"get",url:`/actions/variables/${t}`}).catch(s=>s?.response);a===404&&await this.client({method:"post",url:"/actions/variables",data:{name:t,value:o}}),a===200&&r.name.toLowerCase()===t.toLowerCase()&&await this.client({method:"patch",url:`/actions/variables/${t}`,data:{name:t,value:o}});}uploadHook(){throw new Err("Github hooks not supported yet")}};var I=!0,E=class extends y{static{c(this,"GitlabService");}projectId;projectName;projectPath;projectCredsUrl;projectCredsOwner;token;server;force=!0;log=createLogger(this.constructor.name);constructor(t){super(t),this.assign(t),this.checkConfig(),this.client=this.createClient(t.clientOptions);}checkConfig(){if(!this.projectId)throw new Err("!projectId");if(!this.server)throw new Err("!server");if(!this.projectName)throw new Err("!projectName");if(!this.projectPath)throw new Err("!projectPath");if(!this.projectCredsUrl)throw new Err("!projectCredsUrl");if(!this.projectCredsOwner)throw new Err("!projectCredsOwner");if(!this.token)throw new Err("!token")}createClient(t={}){return K.create({baseURL:`https://${this.server}/api/v4/projects/${this.projectId}`,headers:{"PRIVATE-TOKEN":this.token},...t})}getServiceHostname(){return this.server}getProjectUrl(){return `https://${this.getServiceHostname()}/${this.projectPath}`}getProjectCICDSettingURL(){return `${this.getProjectUrl()}/-/settings/ci_cd`}async uploadVariableOrSecret(t,e,o={}){let r=typeof e=="string"?e:e.value,a=e?.type||o?.type||"file",s=!!(e?.protected??o?.protected??!1),{data:n}=await this.client({method:"get",url:`/variables/${t}`}).catch(h=>{return {data:{value:"@lskjs/creds"}}});if(n.value&&n.value.indexOf("@lskjs/creds")===-1&&!I){this.log.warn(`[IGNORE] Project ${this.projectId} ${t}`);return}await this.client({method:"delete",url:`/variables/${t}`}).catch(()=>{}),await this.client({method:"post",url:"/variables",data:{key:t,value:r,variable_type:a,protected:s,...o}});}async uploadSecret(t,e,o={}){return this.uploadVariableOrSecret(t,e,{type:"file",protected:!!(o.protected??!0)})}async uploadVariable(t,e,o={}){return this.uploadVariableOrSecret(t,e,{type:"env_var",protected:!!(o.protected??!1)})}async uploadEnv(){throw this.log.warn("GitLab uploading env doesn't supported"),new Err("NOT_IMPLEMENTED")}async removeOldHooks(){let{data:t}=await this.client({method:"get",url:"/hooks"}).catch(e=>{return {data:{value:"@lskjs/creds"}}});await map(t,async({id:e})=>{await this.client({method:"delete",url:`/hooks/${e}`});});}async uploadHook(t){await this.client({method:"post",url:"/hooks",data:t});}};async function x(i,{removeCache:t}={}){try{let e=b(i);return t&&delete b.cache[b.resolve(i)],e}catch(e){if(Err.getCode(e)==="MODULE_NOT_FOUND")throw new Err(`${i} not found`,e);if(Err.getCode(e).startsWith("Dynamic require of")||Err.getCode(e).startsWith("ReferenceError: module is not defined in ES module scope"))try{return await import(i)}catch(r){throw Err.getCode(r)==="ERR_MODULE_NOT_FOUND"?new Err(`${i} not found`,r):new Err("importErr",r)}throw new Err("requireErr",e)}}c(x,"importRequire");async function L(i){let t=`${i}/config.js`,e=await x(t);return {path:t,config:e.default||e}}c(L,"loadConfig");async function O(i,t={}){let{path:e,config:o}=await L(i),r=o.service?.serviceName;if(!r)throw new Err("!serviceName",{data:{configPath:e}});let a,s={...o.service,...t,config:o};if(r==="github")a=new k(s);else if(r==="gitlab")a=new E(s);else throw new Err("incorrect serviceName",{serviceName:r});return a}c(O,"createService");async function R(i,t={}){let e=t.log||w,o=t.buildDir||`${i}/build`,r=await O(i,t);await unlink(`${o}`).catch(()=>{}),await mkdir(o,{recursive:!0});let{config:a}=r,{files:s=[]}=a;await mapSeries(s,async n=>{let{filename:h,handler:f}=n,d=await f(n,a),{credType:C,name:P}=n,S=getComment({filename:h,values:[["File type",n.type],["Cred type",C],["Cred name",P],["Server",r.getServiceHostname()],["Project",r.getProjectPath()],["Project ID",r.getProjectId()],["Project Url",r.getProjectUrl()],["CI/CD Setting",r.getProjectCICDSettingURL()]],footer:r.getProjectCredsUrl()&&`
Auto generated by ${r.getProjectCredsUrl()}
If you want to change something, please contact admin repo: ${r.getProjectCredsOwner()}.
`.trim()}),H=n.format||n.type,D=`${o}/${h}`,{status:T}=await jsonToFile(D,d,{format:H,compare:!t.force,comment:S}),V=relative(process.cwd(),D);e.info(`[${T}] ${r.getProjectPath()} (${P})[${C}] => ${V}`);});}c(R,"build");async function v(i){let t=await readdir(i,{withFileTypes:!0});return (await map(t,async o=>o.isDirectory()?[{name:o.name,dir:resolve(i),filename:resolve(i,o.name)},...await v(resolve(i,o.name))]:[])).flat()}c(v,"getDirs");async function be(i,t={}){let e=await v(i),o=(await map(e,async r=>{let{filename:a}=r;return await existsSync(`${a}/index.js`)?r:null})).filter(Boolean);return mapSeries(o,async({filename:r})=>{await R(r,t).catch(a=>{w.error(`Build error ${r}: `,Err.getMessage(a)),w.error(a);});})}c(be,"buildDeep");async function M(i,t){let e=t.buildDir||`${i}/build`,o=await O(i,t),{config:r}=o,{files:a=[],variables:s,secrets:n,hooks:h}=r,f=await map(a,async d=>{let{filename:C}=d,P=await readFile(`${e}/${C}`).then(S=>S.toString());return {...d,content:P}});await o.uploadAll({files:f,variables:s,secrets:n,hooks:h});}c(M,"upload");async function Te(i,t={}){let e=await v(i),o=(await map(e,async r=>{let{filename:a}=r;return await existsSync(`${a}/index.js`)?r:null})).filter(Boolean);return mapSeries(o,async({filename:r})=>{await M(r,t).catch(a=>{w.error(`Build error ${r}: `,Err.getMessage(a)),w.error(a);});})}c(Te,"uploadDeep");
export { R as build, be as buildDeep, M as upload, Te as uploadDeep };
//# sourceMappingURL=out.js.map
//# sourceMappingURL=index.js.map