Skip to content
This repository was archived by the owner on May 20, 2025. It is now read-only.

Commit fc5828a

Browse files
authored
feat: common: more rigorous uses: handling (#43)
1 parent db093f5 commit fc5828a

File tree

1 file changed

+32
-42
lines changed

1 file changed

+32
-42
lines changed

src/common.rs

Lines changed: 32 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -203,36 +203,13 @@ impl FromStr for Uses {
203203
#[derive(Debug, PartialEq)]
204204
pub struct LocalUses {
205205
pub path: String,
206-
pub git_ref: Option<String>,
207206
}
208207

209208
impl FromStr for LocalUses {
210209
type Err = UsesError;
211210

212211
fn from_str(uses: &str) -> Result<Self, Self::Err> {
213-
let (path, git_ref) = match uses.rsplit_once('@') {
214-
Some((path, git_ref)) => (path, Some(git_ref)),
215-
None => (uses, None),
216-
};
217-
218-
if path.is_empty() {
219-
return Err(UsesError(format!(
220-
"local uses has no path component: {uses}"
221-
)));
222-
}
223-
224-
// TODO: Overly conservative? `uses: ./foo/bar@` might be valid if
225-
// `./foo/bar@/action.yml` exists.
226-
if git_ref.is_some_and(|git_ref| git_ref.is_empty()) {
227-
return Err(UsesError(format!(
228-
"local uses is missing git ref after '@': {uses}"
229-
)));
230-
}
231-
232-
Ok(LocalUses {
233-
path: path.into(),
234-
git_ref: git_ref.map(Into::into),
235-
})
212+
Ok(LocalUses { path: uses.into() })
236213
}
237214
}
238215

@@ -259,9 +236,8 @@ impl FromStr for RepositoryUses {
259236
// In theory we could do `From<String>` instead, but
260237
// `&mut str::split_mut` and similar don't exist yet.
261238

262-
// NOTE: Technically both git refs and action paths can contain `@`,
263-
// so this isn't guaranteed to be correct. In practice, however,
264-
// splitting on the last `@` is mostly reliable.
239+
// NOTE: Both git refs and paths can contain `@`, but in practice
240+
// GHA refuses to run a `uses:` clause with more than one `@` in it.
265241
let (path, git_ref) = match uses.rsplit_once('@') {
266242
Some((path, git_ref)) => (path, Some(git_ref)),
267243
None => (uses, None),
@@ -362,12 +338,29 @@ where
362338
let uses = step_uses(de)?;
363339

364340
match uses {
365-
Uses::Repository(repo) if repo.git_ref.is_none() => Err(de::Error::custom(
366-
"repo action must have `@<ref> in reusable workflow",
367-
)),
368-
// NOTE: local reusable workflows do not have to be pinned.
369-
Uses::Local(_) => Ok(uses),
370-
Uses::Repository(_) => Ok(uses),
341+
Uses::Repository(ref repo) => {
342+
// Remote reusable workflows must be pinned.
343+
if repo.git_ref.is_none() {
344+
Err(de::Error::custom(
345+
"repo action must have `@<ref>` in reusable workflow",
346+
))
347+
} else {
348+
Ok(uses)
349+
}
350+
}
351+
Uses::Local(ref local) => {
352+
// Local reusable workflows cannot be pinned.
353+
// We do this with a string scan because `@` *can* occur as
354+
// a path component in local actions uses, just not local reusable
355+
// workflow uses.
356+
if local.path.contains('@') {
357+
Err(de::Error::custom(
358+
"local reusable workflow reference can't specify `@<ref>`",
359+
))
360+
} else {
361+
Ok(uses)
362+
}
363+
}
371364
// `docker://` is never valid in reusable workflow uses.
372365
Uses::Docker(_) => Err(de::Error::custom(
373366
"docker action invalid in reusable workflow `uses`",
@@ -555,19 +548,17 @@ mod tests {
555548
})),
556549
),
557550
(
558-
// Valid: Local action ref
551+
// Valid: Local action "ref", actually part of the path
559552
"./.github/actions/hello-world-action@172239021f7ba04fe7327647b213799853a9eb89",
560553
Ok(Uses::Local(LocalUses {
561-
path: "./.github/actions/hello-world-action".to_owned(),
562-
git_ref: Some("172239021f7ba04fe7327647b213799853a9eb89".to_owned()),
554+
path: "./.github/actions/hello-world-action@172239021f7ba04fe7327647b213799853a9eb89".to_owned(),
563555
})),
564556
),
565557
(
566558
// Valid: Local action ref, unpinned
567559
"./.github/actions/hello-world-action",
568560
Ok(Uses::Local(LocalUses {
569561
path: "./.github/actions/hello-world-action".to_owned(),
570-
git_ref: None,
571562
})),
572563
),
573564
// Invalid: missing user/repo
@@ -616,13 +607,12 @@ mod tests {
616607
git_ref: Some("abcd".to_owned()),
617608
})),
618609
),
619-
// Valid: local reusable workflow
610+
// Invalid: remote reusable workflow without ref
611+
("octo-org/this-repo/.github/workflows/workflow-1.yml", None),
612+
// Invalid: local reusable workflow with ref
620613
(
621614
"./.github/workflows/workflow-1.yml@172239021f7ba04fe7327647b213799853a9eb89",
622-
Some(Uses::Local(LocalUses {
623-
path: "./.github/workflows/workflow-1.yml".to_owned(),
624-
git_ref: Some("172239021f7ba04fe7327647b213799853a9eb89".to_owned()),
625-
})),
615+
None,
626616
),
627617
// Invalid: no ref at all
628618
("octo-org/this-repo/.github/workflows/workflow-1.yml", None),

0 commit comments

Comments
 (0)