renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
169 lines (168 loc) • 4.99 kB
JavaScript
import { regEx } from "../../../util/regex.js";
import is from "@sindresorhus/is";
//#region lib/modules/manager/github-actions/parse.ts
function splitFirstFrom(str, sep, start) {
const idx = str.indexOf(sep, start);
if (idx === -1) return null;
return [str.slice(0, idx), str.slice(idx + sep.length)];
}
function splitFirst(str, sep) {
return splitFirstFrom(str, sep, 0);
}
function parseQuote(input) {
const trimmed = input.trim();
const first = trimmed[0];
const last = trimmed[trimmed.length - 1];
if (trimmed.length >= 2 && first === last && (first === "\"" || first === "'")) return {
value: trimmed.slice(1, -1),
quote: first
};
return {
value: trimmed,
quote: ""
};
}
const shaRe = regEx(/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/);
const shaShortRe = regEx(/^[a-f0-9]{6,7}$/);
function isSha(str) {
return shaRe.test(str);
}
function isShortSha(str) {
return shaShortRe.test(str);
}
const DOCKER_PREFIX = "docker://";
function parseDockerReference(input) {
const originalRef = input.slice(9);
if (!originalRef) return null;
const digestParts = splitFirst(originalRef, "@");
if (digestParts) {
const [image, digest] = digestParts;
return {
kind: "docker",
image,
digest,
originalRef
};
}
const lastSlashIndex = originalRef.lastIndexOf("/");
const tagParts = splitFirstFrom(originalRef, ":", lastSlashIndex === -1 ? 0 : lastSlashIndex + 1);
if (tagParts) {
const [image, tag] = tagParts;
return {
kind: "docker",
image,
tag,
originalRef
};
}
return {
kind: "docker",
image: originalRef,
originalRef
};
}
const repositoryActionRegex = regEx(/^(?:https:\/\/(?<hostname>[^/]+)\/)?(?<owner>[^/]+)\/(?<repo>[^/]+)(?:\/(?<path>.+?))?@(?<ref>.+)$/);
function parseRepositoryReference(input) {
const match = repositoryActionRegex.exec(input);
if (!match?.groups) return null;
const { owner, repo, path, ref } = match.groups;
let { hostname } = match.groups;
let isExplicitHostname = true;
if (is.undefined(hostname)) {
hostname = "github.com";
isExplicitHostname = false;
}
return {
kind: "repository",
hostname,
isExplicitHostname,
owner,
repo,
path,
ref
};
}
function parseActionReference(uses) {
if (!uses) return null;
if (uses.startsWith(DOCKER_PREFIX)) return parseDockerReference(uses);
if (uses.startsWith("./") || uses.startsWith("../")) return {
kind: "local",
path: uses
};
return parseRepositoryReference(uses);
}
const pinTokenRe = regEx(/^\s*(?:(?:renovate\s*:\s*)?(?:pin\s+|tag\s*=\s*)?|(?:ratchet:[\w-]+\/[.\w-]+))?@?(?<version>([\w-]*[-/])?v?\d+(?:\.\d+(?:\.\d+)?)?(?:-[a-zA-Z0-9.]+)?)/);
const versionLikeRe = regEx(/^v?\d+/);
const bareTokenRe = regEx(/^\s*(?<token>\S+)\s*$/);
function parseComment(commentBody) {
if (commentBody.trim() === "ratchet:exclude") return { ratchetExclude: true };
const match = pinTokenRe.exec(commentBody);
if (match?.groups?.version) return {
pinnedVersion: match.groups.version,
matchedString: match[0],
index: match.index
};
const bareMatch = bareTokenRe.exec(commentBody);
if (bareMatch?.groups?.token) return {
ref: bareMatch.groups.token,
matchedString: bareMatch[0],
index: bareMatch.index
};
return {};
}
const usesLineRegex = regEx(/^(?<prefix>\s+(?:-\s+)?uses\s*:\s*)(?<remainder>.+)$/);
/**
* Parses a GitHub Actions `uses:` line into its components.
*
* Expected line format:
* ```
* <indentation>[- ]uses: [quote]<action-reference>[quote][ # <comment>]
* ```
*
* Examples:
* - ` uses: actions/checkout@v4`
* - ` - uses: "owner/repo@abc123" # v1.0.0`
* - ` uses: docker://alpine:3.18`
*
* @returns Parsed components or `null` if the line doesn't match `uses:` pattern
*/
function parseUsesLine(line) {
const match = usesLineRegex.exec(line);
if (!match?.groups) return null;
const { prefix, remainder } = match.groups;
if (remainder.startsWith("#")) return null;
const indentation = prefix.slice(0, prefix.indexOf("uses"));
const commentIndex = remainder.indexOf(" #");
if (commentIndex === -1) {
const { value, quote } = parseQuote(remainder);
return {
indentation,
usesPrefix: prefix,
replaceString: remainder.trim(),
commentPrecedingWhitespace: "",
commentString: "",
actionRef: parseActionReference(value),
commentData: {},
quote
};
}
const rawValuePart = remainder.slice(0, commentIndex);
const commentPart = remainder.slice(commentIndex + 1);
const partBeforeHash = remainder.slice(0, commentIndex + 1);
const commentPrecedingWhitespace = partBeforeHash.slice(partBeforeHash.trimEnd().length);
const { value, quote } = parseQuote(rawValuePart);
const cleanCommentBody = commentPart.slice(1);
return {
indentation,
usesPrefix: prefix,
replaceString: rawValuePart.trim(),
commentPrecedingWhitespace,
commentString: commentPart,
actionRef: parseActionReference(value),
commentData: parseComment(cleanCommentBody),
quote
};
}
//#endregion
export { isSha, isShortSha, parseUsesLine, versionLikeRe };
//# sourceMappingURL=parse.js.map