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