Skip to content

Instantly share code, notes, and snippets.

@gparlakov
Last active April 20, 2020 15:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gparlakov/426820697dc2574da6e6f6f6b31d5498 to your computer and use it in GitHub Desktop.
Save gparlakov/426820697dc2574da6e6f6f6b31d5498 to your computer and use it in GitHub Desktop.
Get and memory-unzip Azure Dev Ops logs from a Release Pipeline
/// <reference types="node" />
import axios from 'axios';
import * as yauzl from 'yauzl';
const headers = {
Authorization: `Basic ${Buffer.from(`PAT:${this.token}`).toString('base64')}`,
'X-TFS-FedAuthRedirect': 'Suppress', // we can't handle auth redirect so - suppress
};
const accessToken = process.env.AZURE_ACCESS_TOKEN;
const project = 'my project'; // TODO Replace with your own
const organization = 'my organization name'; // TODO Replace with your own
if (accessToken == null || accessToken === '') {
throw new Error('Please provide an access token');
} else {
console.log('token is present!');
}
export const axiosInstance = axios.create({
baseURL: `https://vsrm.dev.azure.com/${organization}/${project}/_apis/`,
headers: headers,
});
const releaseId = 1; // TODO Replace with your own
axiosInstance.get(`release/releases/${releaseId}/logs`, {
responseType: 'stream',
}).then(logs => {
if (logs.status != 200) {
throw new Error('logs missing');
}
return readLogs(logs.data)
})
.then(({logs}) => {
console.log(logs);
});
function readLogs(
zipBuffer: NodeJS.ReadableStream,
): Promise<{ logs: string; skippedFor: Error[] }> {
// we'll reject the promise when we can't read anything from the zip
// and resolve it when we could read (some) plus add the errors for the skipped parts
// in the end we'd like to say - yes the logs contain the Proof OR no the logs do not contain the proof but there were skipped parts
return new Promise((res, rej) => {
const es: Error[] = [];
const zipChunks: any[] = [];
zipBuffer.on('data', d => zipChunks.push(d));
zipBuffer.on('end', () => {
yauzl.fromBuffer(Buffer.concat(zipChunks), { lazyEntries: true }, function(err, zipfile) {
// can not even open the archive just reject the promise
if (err) {
rej(err);
}
if (zipfile != null) {
const chunks: any[] = [];
zipfile.on('entry', function(entry) {
if (/\/$/.test(entry.fileName)) {
// Directory file names end with '/'.
// Note that entries for directories themselves are optional.
// An entry's fileName implicitly requires its parent directories to exist.
zipfile.readEntry();
} else {
// file entry
zipfile.openReadStream(entry, function(err, readStream) {
if (err) {
es.push(err);
// skip this one - could not read it from zip
zipfile.readEntry();
}
if (readStream == null) {
// just skip - could not get a read stream from it
es.push(
new Error(
'Could not create a readable stream for the log ' + (entry || {}).fileName ||
'<missing file name>',
),
);
zipfile.readEntry();
} else {
readStream.on('data', c => chunks.push(c));
readStream.on('error', e => {
es.push(e);
// skip this one - could not read it from zip
zipfile.readEntry();
});
readStream.on('end', function() {
zipfile.readEntry();
});
}
});
}
});
zipfile.once('end', function() {
zipfile.close();
res({ logs: Buffer.concat(chunks).toString('utf8'), skippedFor: es });
});
zipfile.readEntry();
} else {
// can't read the archive - reject the promise
rej(new Error('Could not read the zipfile contents'));
}
});
});
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment