Skip to content

Commit

Permalink
Add support for reusable workflows
Browse files Browse the repository at this point in the history
  • Loading branch information
mheap committed Oct 20, 2022
1 parent 8e88746 commit fc3242d
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 24 deletions.
32 changes: 20 additions & 12 deletions extractActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function extractFromComposite(input, allowEmpty) {
const actions = new Set();
steps = steps[0].value.items;
for (let step of steps) {
handleStep(actions, step);
handleStep(actions, step.items);
}
return Array.from(actions);
}
Expand All @@ -41,18 +41,26 @@ function extractFromWorkflow(input, allowEmpty) {
}

for (let job of jobs) {
let steps = job.value.items.filter((n) => n.key == "steps");
// Check for
let steps = job.value.items.filter(
(n) => n.key == "steps" || n.key == "uses"
);
if (!steps.length) {
throw new Error("No job.steps found");
throw new Error("No job.steps or job.uses found");
}

steps = steps[0].value.items;
if (!steps.length) {
throw new Error("No job.steps found");
}

for (let step of steps) {
handleStep(actions, step);
// It's a job with steps
if (steps[0].value.items) {
if (!steps[0].value.items.length) {
throw new Error("No job.steps found");
}

for (let step of steps[0].value.items) {
handleStep(actions, step.items);
}
} else {
// It's a job that calls a reusable workflow
handleStep(actions, steps);
}
}

Expand All @@ -63,8 +71,8 @@ function extractFromWorkflow(input, allowEmpty) {
return Array.from(actions);
}

function handleStep(actions, step) {
const uses = step.items.filter((n) => n.key == "uses");
function handleStep(actions, items) {
const uses = items.filter((n) => n.key == "uses");

for (let use of uses) {
const line = use.value.value.toString();
Expand Down
25 changes: 24 additions & 1 deletion extractActions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ test("throws with missing steps", () => {
});

const actual = () => extractActions(input);
expect(actual).toThrow("No job.steps found");
expect(actual).toThrow("No job.steps or job.uses found");
});

test("throws with empty steps", () => {
Expand Down Expand Up @@ -367,6 +367,29 @@ test("extracts from composite actions", () => {
]);
});

test("extracts from reusable workflows", () => {
const input = convertToAst({
name: "Sample Reusable",
jobs: {
test: {
uses: "mheap/test-action@master",
}
},
});

const actual = extractActions(input);

expect(actual).toEqual([
{
owner: "mheap",
repo: "test-action",
path: "",
currentVersion: "master",
pinnedVersion: "master",
},
]);
});

function convertToAst(input) {
return YAML.parseDocument(YAML.stringify(input));
}
46 changes: 35 additions & 11 deletions replaceActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ module.exports = function (input, action) {
if (runs.length) {
return replaceInComposite(input, action);
}

return replaceInWorkflow(input, action);
};

function replaceInReusable(input, action) {}

function replaceInComposite(input, action) {
let actionString = `${action.owner}/${action.repo}`;
if (action.path) {
Expand Down Expand Up @@ -48,18 +49,41 @@ function replaceInWorkflow(input, action) {
.items;

for (let job of jobs) {
const steps = job.value.items.filter((n) => n.key == "steps")[0].value
.items;
for (let step of steps) {
const uses = step.items.filter((n) => n.key == "uses");
for (let use of uses) {
if (use.value.value == actionString) {
use.value.value = replacement;
use.value.comment = ` pin@${action.pinnedVersion}`;
}
}
const stepKeys = job.value.items.filter((n) => n.key == "steps");
const usesKeys = job.value.items.filter((n) => n.key == "uses");

if (stepKeys.length) {
replaceStandard(
stepKeys[0].value.items,
actionString,
replacement,
action
);
} else if (usesKeys.length) {
replaceReusable(usesKeys, actionString, replacement, action);
}
}

return input;
}

function replaceReusable(uses, actionString, replacement, action) {
for (let use of uses) {
if (use.value.value == actionString) {
use.value.value = replacement;
use.value.comment = ` pin@${action.pinnedVersion}`;
}
}
}

function replaceStandard(steps, actionString, replacement, action) {
for (let step of steps) {
const uses = step.items.filter((n) => n.key == "uses");
for (let use of uses) {
if (use.value.value == actionString) {
use.value.value = replacement;
use.value.comment = ` pin@${action.pinnedVersion}`;
}
}
}
}
15 changes: 15 additions & 0 deletions replaceActions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,21 @@ test("replaces a single action with a sha (composite)", () => {
expect(actual).toContain("uses: mheap/test-action@sha-here # pin@master");
});

test("replaces a single action with a sha (reusable)", () => {
const input = convertToAst({
name: "Sample Reusable",
jobs: {
test: {
uses: "mheap/test-action@master",
}
},
});

const actual = replaceActions(input, { ...action }).toString();

expect(actual).toContain("uses: mheap/test-action@sha-here # pin@master");
});

test("replaces an existing sha with a different sha, not changing the pinned branch", () => {
const input = convertToAst({
name: "PR",
Expand Down

0 comments on commit fc3242d

Please sign in to comment.