UNPKG

clever-tools

Version:

Command Line Interface for Clever Cloud.

155 lines (141 loc) 4.33 kB
import cliparse from 'cliparse'; import * as git from 'isomorphic-git'; import _ from 'lodash'; import fs from 'node:fs'; import path from 'node:path'; import { slugify } from '../lib/slugify.js'; import { loadOAuthConf } from './configuration.js'; import { findPath } from './fs-utils.js'; import * as http from './isomorphic-http-with-agent.js'; async function getRepo() { try { const dir = await findPath('.', '.git'); return { fs, dir, http }; } catch { throw new Error('Could not find the .git folder.'); } } async function onAuth() { const tokens = await loadOAuthConf(); return { username: tokens.token, password: tokens.secret, }; } export async function addRemote(remoteName, url) { const repo = await getRepo(); const safeRemoteName = slugify(remoteName); const allRemotes = await git.listRemotes({ ...repo }); const existingRemote = _.find(allRemotes, { remote: safeRemoteName }); if (existingRemote == null) { // In some situations, we may end up with race conditions so we force it return git.addRemote({ ...repo, remote: safeRemoteName, url, force: true }); } } export async function resolveFullCommitId(commitId) { if (commitId == null) { return null; } try { const repo = await getRepo(); return await git.expandOid({ ...repo, oid: commitId }); } catch (e) { if (e.code === 'ShortOidNotFound') { throw new Error(`Commit id ${commitId} is ambiguous`); } throw e; } } export async function getRemoteCommit(remoteUrl) { const repo = await getRepo(); const remoteInfos = await git.getRemoteInfo({ ...repo, onAuth, url: remoteUrl, }); return _.get(remoteInfos, 'refs.heads.master'); } export async function getFullBranch(branchName) { const repo = await getRepo(); if (branchName === '') { const currentBranch = await git.currentBranch({ ...repo, fullname: true }); return currentBranch || 'HEAD'; } return git.expandRef({ ...repo, ref: branchName }); } export async function getBranchCommit(refspec) { const repo = await getRepo(); const oid = await git.resolveRef({ ...repo, ref: refspec }); // When a refspec refers to an annotated tag, the OID ref represents the annotation and not the commit directly, // that's why we need a call to `readCommit`. const res = await git.readCommit({ ...repo, ref: refspec, oid }); return res.oid; } export async function isExistingTag(tag) { const repo = await getRepo(); const tags = await git.listTags({ ...repo, }); return tags.includes(tag); } export async function push(remoteUrl, branchRefspec, force) { const repo = await getRepo(); try { const push = await git.push({ ...repo, onAuth, url: remoteUrl, ref: branchRefspec, remoteRef: 'master', force, }); if (push.errors != null) { throw new Error(push.errors.join(', ')); } return push; } catch (e) { if (e.code === 'PushRejectedNonFastForward') { throw new Error('Push rejected because it was not a simple fast-forward. Use "--force" to override.'); } throw e; } } export function completeBranches() { return getRepo() .then((repo) => git.listBranches(repo)) .then(cliparse.autocomplete.words); } export async function isShallow() { const { dir } = await getRepo(); try { await fs.promises.access(path.join(dir, '.git', 'shallow')); return true; } catch { return false; } } /** * Check if the current directory is a git repository * @returns {Promise<boolean>} */ export async function isInsideGitRepo() { return getRepo() .then(() => true) .catch(() => false); } /** * Check if the current git working directory is clean * @returns {Promise<boolean>} */ export async function isGitWorkingDirectoryClean() { const repo = await getRepo(); const status = await git.statusMatrix({ ...repo }); const isStatusEmpty = status.filter(([filepath, head, workdir]) => { // WARNING: isomorphic-git does not support global gitignore so we filter hidden files and dirs to reduce the amount of false positives const isHidden = filepath.startsWith('.'); const isCleverJson = filepath === '.clever.json'; return (!isHidden || isCleverJson) && head !== workdir; }).length === 0; return isStatusEmpty; }