Skip to content

Commit

Permalink
Added support for include files
Browse files Browse the repository at this point in the history
  • Loading branch information
firecow committed Jun 16, 2020
1 parent b926257 commit 33e64e4
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 67 deletions.
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,6 @@
"pkg": "^4.4.8",
"source-map-support": "^0.5.19",
"tslint": "^6.1.2",
"typescript": "^3.9.4"
"typescript": "^3.9.5"
}
}
18 changes: 11 additions & 7 deletions src/commander.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,24 +72,28 @@ export class Commander {
}

static runList(parser: Parser) {
const stageNames = Array.from(parser.getStages().values()).map((s) => s.name);
const jobs = Array.from(parser.getJobs().values()).sort((a, b) => {
const stageNames = Array.from(parser.getStages()).map((s) => s.name);
const jobs = Array.from(parser.getJobs()).sort((a, b) => {
const whenPrio = ["never"];
if (a.stage !== b.stage) {
return stageNames.indexOf(a.stage) - stageNames.indexOf(b.stage);
}
return whenPrio.indexOf(b.when) - whenPrio.indexOf(a.when);
});

const header = `${"Name".padEnd(parser.maxJobNameLength)} ${"When".padEnd(20)} ${"Stage".padEnd(15)} ${"AllowFailure".padEnd(15)} ${"Needs"}`;
process.stdout.write(`${header}\n`);
process.stdout.write(`${new Array(header.length).fill("-").join("")}\n`);

let whenPadEnd = 0;
parser.getJobs().forEach(j => whenPadEnd = Math.max(j.when.length + 1, whenPadEnd));

let stagePadEnd = 0;
parser.getStageNames().forEach(s => stagePadEnd = Math.max(s.length + 1, stagePadEnd));

for (const job of jobs) {
const needs = job.needs;
let jobLine = `${job.getJobNameString()} ${job.when.padEnd(20)} ${c.yellow(`${job.stage.padEnd(15)}`)} ${String(job.allowFailure).padEnd(15)}`;
const allowFailure = job.allowFailure ? 'warning' : ''
let jobLine = `${job.getJobNameString()} ${c.yellow(`${job.stage.padEnd(stagePadEnd)}`)} ${job.when.padEnd(whenPadEnd)} ${allowFailure.padEnd(8)}`;
if (needs) {
jobLine += ` needs: [${c.blueBright(`${needs.join(',')}`)}]`
jobLine += ` [${c.blueBright(`${needs.join(',')}`)}]`
}
process.stdout.write(`${jobLine}\n`);
}
Expand Down
2 changes: 2 additions & 0 deletions src/default_cmd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@ exports.handler = async(argv: any) => {
if (argv.job) {
const pipelineIid = predefinedVariables.getPipelineIid(cwd);
const parser = new Parser(cwd, pipelineIid);
parser.validateNeedsTags();
await Commander.runSingleJob(parser, argv.job as string, argv.needs as boolean);
} else {
predefinedVariables.incrementPipelineIid(cwd);
const pipelineIid = predefinedVariables.getPipelineIid(cwd);
const parser = new Parser(cwd, pipelineIid);
parser.validateNeedsTags();
await Commander.runPipeline(parser, argv.manual as string[] || []);
}
};
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ Array.prototype.first = function() {
};

const argv = yargs
.version("4.1.0")
.version("4.1.2")
.showHelpOnFail(false)
.command(defaultCmd as CommandModule)
.usage("Find more information at https://github.com/firecow/gitlab-ci-local")
.option("manual", {type: "array", description: "One or more manual jobs to run during a pipeline", requiresArg: true})
Expand Down
2 changes: 1 addition & 1 deletion src/job.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export class Job {

this.stageIndex = stages.indexOf(this.stage);
if (this.stageIndex === -1) {
process.stderr.write(`${c.red("Stage index is -1")}\n`);
process.stderr.write(`${c.yellowBright(this.stage)} ${c.red(`not defined for`)} ${c.blueBright(this.name)}\n`);
process.exit(1);
}

Expand Down
103 changes: 49 additions & 54 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class Parser {
return {};
}

return yaml.parse(fs.readFileSync(`${filePath}`, "utf8"));
return yaml.parse(fs.readFileSync(`${filePath}`, "utf8")) || {};
}

private readonly illigalJobNames = [
Expand All @@ -28,67 +28,27 @@ export class Parser {

public readonly maxJobNameLength: number = 0;
private readonly stages: Map<string, Stage> = new Map();
private readonly cwd: string;

public constructor(cwd: any, pipelineIid: number) {
let path = '';
let yamlDataList: any[] = [];

const orderedVariables = [];
const orderedYml = [];
this.cwd = cwd;

path = `${cwd}/.gitlab-ci.yml`;
const gitlabCiData = Parser.loadYaml(path);
yamlDataList = yamlDataList.concat(this.prepareIncludes(gitlabCiData));

// Add .gitlab-ci.yml
let path = `${cwd}/.gitlab-ci.yml`;
if (!fs.existsSync(path)) { // Fail if empty
process.stderr.write(`${cwd}/.gitlab-ci.yml is not found\n`);
process.exit(1);
}
orderedYml.push(Parser.loadYaml(`${cwd}/.gitlab-ci.yml`));
orderedVariables.push(orderedYml.last().variables);

// Add .gitlab-ci-local.yml
path = `${cwd}/.gitlab-ci-local.yml`;
orderedYml.push(Parser.loadYaml(path));
orderedVariables.push(orderedYml.last().variables || {});
if (!orderedYml.last() || Object.keys(orderedYml.last()).length === 0) { // Warn if empty
process.stderr.write(`WARN: ${cwd}/.gitlab-ci-local.yml is empty or not found\n`);
}

// Add included yaml's.
orderedYml.unshift({});
const includes = deepExtend.apply(this, orderedYml).include || [];
for (const value of includes) {
if (value.local) {
orderedYml.unshift(Parser.loadYaml(`${cwd}/${value.local}`));
orderedVariables.unshift(orderedYml.first().variables || {});
} else if (value.file) {
const ref = value.ref || "master";
const file = value.file;
const project = value.project;
const gitlabCiLocalPath = `${cwd}/.gitlab-ci-local/includes/${project}/${ref}/`;

fs.ensureDirSync(gitlabCiLocalPath);
execSync(`git archive [email protected]:${project}.git ${ref} --format=zip ${file} | gunzip -c - > ${file}`, {
cwd: gitlabCiLocalPath
});

orderedYml.unshift(Parser.loadYaml(`${cwd}/.gitlab-ci-local/includes/${project}/${ref}/${file}`));
orderedVariables.unshift(orderedYml.first().variables || {});
} else {
process.stderr.write(`Didn't understand include ${JSON.stringify(value)}\n`);
}
}
const gitlabCiLocalData = Parser.loadYaml(path);
yamlDataList = yamlDataList.concat(this.prepareIncludes(gitlabCiLocalData));

// Setup variables and "merged" yml
orderedYml.unshift({});
const gitlabData = deepExtend.apply(this, orderedYml);

// 'stages' missing, throw error
if (!gitlabData.stages) {
process.stderr.write(`${c.red("'stages' tag is missing")}\n`);
process.exit(1);
}
const gitlabData = deepExtend.apply(this, yamlDataList);

// Generate stages
for (const value of gitlabData.stages) {
for (const value of gitlabData.stages || []) {
this.stages.set(value, new Stage(value));
}

Expand Down Expand Up @@ -120,8 +80,43 @@ export class Parser {

this.jobs.set(key, job);
}
}

private prepareIncludes(doc: any): any[] {
let includeDatas: any[] = [];
const cwd = this.cwd;

for (const value of doc["include"] || []) {
if (value["local"]) {
const localDoc = Parser.loadYaml(`${this.cwd}/${value.local}`);
includeDatas = includeDatas.concat(this.prepareIncludes(localDoc));
} else if (value["file"]) {
const ref = value["ref"] || "master";
const file = value["file"];
const project = value["project"];
const gitlabCiLocalPath = `${cwd}/.gitlab-ci-local/includes/${project}/${ref}/`;

const domainRegExp = /^.*@(.*):.*\(fetch\)/;
const output = execSync(`git remote -v`, {
cwd: `${this.cwd}`
});
const exec = domainRegExp.exec(`${output}`);
const gitDomain = exec ? exec[1] : null;
fs.ensureDirSync(gitlabCiLocalPath);
execSync(`git archive --remote=git@${gitDomain}:${project}.git ${ref} --format=zip ${file} | gunzip -c - > ${file}`, {
cwd: gitlabCiLocalPath
});

const fileDoc = Parser.loadYaml(`${cwd}/.gitlab-ci-local/includes/${project}/${ref}/${file}`);
includeDatas = includeDatas.concat(this.prepareIncludes(fileDoc));

} else {
process.stderr.write(`Didn't understand include ${JSON.stringify(value)}\n`);
}
}

this.validateNeedsTags();
includeDatas.push(doc);
return includeDatas;
}

public getJobByName(name: string): Job {
Expand Down Expand Up @@ -150,7 +145,7 @@ export class Parser {
return Array.from(this.stages.values());
}

private validateNeedsTags() {
public validateNeedsTags() {
const jobNames = Array.from(this.jobs.values()).map((j) => j.name);
for (const job of this.jobs.values()) {
if (job.needs === null || job.needs.length === 0) {
Expand Down

0 comments on commit 33e64e4

Please sign in to comment.