Expand Azure Pipelines model foundation - #393
Conversation
Add structural model coverage for Azure Pipelines documents and templates, including parameters, stages, jobs, checkout/template steps, script variants, and repository/pipeline resources. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8eab11c3-3153-4036-9847-28a2f615835f
c687a1d to
db8aea2
Compare
GitHub Actions workflows share `jobs:`/`steps:` keys with Azure DevOps pipelines, so the shape-based Document match misclassified them. Exclude documents that live under .github/workflows/ or declare a top-level `on:` trigger, and regenerate the AST test expectations. Adds a github-workflow.yml regression input. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Mathias Vorreiter Pedersen (MathiasVP)
left a comment
There was a problem hiding this comment.
Mostly stylistic comments. I think we should revisit all the predicate names / QLDoc for the "getters" implemented in this PR so that it conforms to the standard naming / phrasing conventions (i.e., the comment I put here: https://github.com/microsoft/codeql/pull/393/changes#r3926233771), but otherwise this all looks good!
| /** | ||
| * Gets a top-level trigger-like entry. | ||
| */ | ||
| YamlValue getTrigger(string name) { result = this.lookup(name) } |
There was a problem hiding this comment.
| /** | |
| * Gets a top-level trigger-like entry. | |
| */ | |
| YamlValue getTrigger(string name) { result = this.lookup(name) } | |
| /** | |
| * Gets the top-level trigger-like entry named `name`, if any. | |
| */ | |
| YamlValue getTrigger(string name) { result = this.lookup(name) } |
| /** | ||
| * Gets the pipeline parameters. | ||
| */ | ||
| Parameter getParameters() { result = this.lookup("parameters").getAChild() } |
There was a problem hiding this comment.
There's some QLDoc terminology that I'd really like us to switch to. A predicate can essentially fall into 4 categories:
- It always has 1 result
- It has at most 1 result (i.e., 0 or 1 result)
- It has 0 or more results
- It has 1 or more results
Each of these comes with recommended phrasing for the name of the predicate, and phrasing for the QLDoc:
- If the predicate always has 1 result it should be named
getX(for some appropriateX), and its QLDoc should be written asGets the X. For example:/** * Gets the parameter associated with this `Document`. */ Parameter getParameter() { result = .... }
- If the predicate has at most 1 result (i.e., 0 or 1) it should be named
getX, and its QLDoc should be written asGets the X, if any.. For example:/** * Gets the parameter associated with this `Document`, if any. */ Parameter getParameter() { result = .... }
- If the predicate has 0 or more results it should be named
getAX(orgetAnX), and its QLDoc should be written asGets an X, if any.. For example:/** * Gets a parameter associated with this `Document`, if any. */ Parameter getAParameter() { result = .... }
- If the predicate has 1 or more results it should be named
getAX(orgetAnX), and its QLDoc should be written asGets an X.. For example:/** * Gets a parameter associated with this `Document`. */ Parameter getAParameter() { result = .... }
The name getParameters doesn't fall into any of these 4 categories. So I don't know how many results I can expect from this query!
| class Stage extends YamlNode, YamlMapping { | ||
| Stage() { exists(Document document | document.lookup("stages").getAChildNode() = this) } | ||
|
|
||
| override string toString() { result = "Stage '" + this.getName() + "'" } |
| exists(Stage stage | stage.lookup("jobs").getAChildNode() = this) | ||
| } | ||
|
|
||
| override string toString() { result = "Job '" + this.getName() + "'" } |
There was a problem hiding this comment.
Same here. I think this should just be result = this.getName().
| exists(Document document | document.lookup("steps").getAChildNode() = this | result = document) | ||
| or | ||
| exists(Document document | this.getFile() = document.getFile() | result = document) |
There was a problem hiding this comment.
What a convoluted way to say this:
| exists(Document document | document.lookup("steps").getAChildNode() = this | result = document) | |
| or | |
| exists(Document document | this.getFile() = document.getFile() | result = document) | |
| result.lookup("steps").getAChildNode() = this | |
| or | |
| this.getFile() = result.getFile() |
| exists(Document document | | ||
| document.lookup("resources").(YamlMapping).lookup("repositories").getAChildNode() = this | ||
| | | ||
| result = document | ||
| ) |
There was a problem hiding this comment.
| exists(Document document | | |
| document.lookup("resources").(YamlMapping).lookup("repositories").getAChildNode() = this | |
| | | |
| result = document | |
| ) | |
| result.lookup("resources").(YamlMapping).lookup("repositories").getAChildNode() = this |
| exists(Document document | | ||
| document.lookup("resources").(YamlMapping).lookup("pipelines").getAChildNode() = this | ||
| | | ||
| result = document | ||
| ) |
There was a problem hiding this comment.
| exists(Document document | | |
| document.lookup("resources").(YamlMapping).lookup("pipelines").getAChildNode() = this | |
| | | |
| result = document | |
| ) | |
| result.lookup("resources").(YamlMapping).lookup("pipelines").getAChildNode() = this |

Add structural model coverage for Azure Pipelines documents and templates, including parameters, stages, jobs, checkout/template steps, script variants, and repository/pipeline resources.