|
'use strict'; |
|
|
|
const fs = require('fs').promises; |
|
const { spawn } = require('child_process'); |
|
|
|
async function waitForEnd(process) { |
|
let stderr = ''; |
|
|
|
process.stderr.on('data', data => { |
|
stderr += data.toString(); |
|
}); |
|
|
|
await new Promise(resolve => { |
|
process.on('exit', resolve); |
|
}); |
|
|
|
if (process.exitCode) { |
|
throw new Error(stderr); |
|
} |
|
} |
|
|
|
async function getRemoteTags({ |
|
remote, |
|
cwd, |
|
}) { |
|
let process = spawn('git', ['ls-remote', '--tags', remote], { |
|
cwd, |
|
}); |
|
|
|
let stdout = ''; |
|
|
|
process.stdout.on('data', data => { |
|
stdout += data.toString(); |
|
}); |
|
|
|
await waitForEnd(process); |
|
|
|
let lines = stdout.split(/\r?\n/); |
|
|
|
let tags = lines.reduce((tags, line) => { |
|
let match = line.match(/\w{40}\trefs\/tags\/(.+)\^\{\}*/); |
|
|
|
if (match) { |
|
tags.push(match[1]); |
|
} |
|
|
|
return tags; |
|
}, []); |
|
|
|
return tags; |
|
} |
|
|
|
function difference(setA, setB) { |
|
let _difference = new Set(setA); |
|
for (let elem of setB) { |
|
_difference.delete(elem); |
|
} |
|
return [..._difference]; |
|
} |
|
|
|
async function getLocalTags({ |
|
cwd, |
|
}) { |
|
let process = spawn('git', ['tag'], { |
|
cwd, |
|
}); |
|
|
|
let stdout = ''; |
|
|
|
process.stdout.on('data', data => { |
|
stdout += data.toString(); |
|
}); |
|
|
|
await waitForEnd(process); |
|
|
|
let tags = stdout.split(/\r?\n/); |
|
|
|
return tags; |
|
} |
|
|
|
async function run({ |
|
remote, |
|
cwd = process.cwd(), |
|
} = {}) { |
|
let localTagsPromise = getLocalTags({ |
|
cwd, |
|
}); |
|
let remoteTagsPromise = getRemoteTags({ |
|
remote, |
|
cwd, |
|
}); |
|
|
|
let [ |
|
localTags, |
|
remoteTags, |
|
] = await Promise.all([ |
|
localTagsPromise, |
|
remoteTagsPromise, |
|
]); |
|
|
|
let remoteTagsToRemove = difference(remoteTags, localTags); |
|
|
|
if (!remoteTagsToRemove.length) { |
|
console.log('nothing to do'); |
|
|
|
return; |
|
} |
|
|
|
let deleteProcess = spawn('git', ['push', remote, '-d', ...remoteTagsToRemove], { |
|
cwd, |
|
}); |
|
|
|
deleteProcess.stdout.pipe(process.stdout); |
|
deleteProcess.stderr.pipe(process.stderr); |
|
|
|
await waitForEnd(deleteProcess); |
|
} |
|
|
|
(async () => { |
|
await run({ |
|
remote: process.argv[2], |
|
}); |
|
})(); |
|
|
|
// https://medium.com/@dtinth/making-unhandled-promise-rejections-crash-the-node-js-process-ffc27cfcc9dd |
|
process.on('unhandledRejection', (up) => { |
|
throw up; |
|
}); |