Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for validating one job in a workflow #27

Merged
merged 1 commit into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions SPECIFICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,20 @@ error.

If the checksum file exists the process shall read and parse it fully. If this
fails the process shall exit immediately. Else it shall recompute the checksums
(see [Computing Checksums]) for all actions in the repository using the same
hashing algorithm as was used for the stored checksums. It shall then compare
the computed checksums against the stored checksums.
(see [Computing Checksums]) for all actions in the target using the same hashing
algorithm as was used for the stored checksums. It shall compare the computed
checksums against the stored checksums.

If any of the checksums does not match or is missing the process shall exit with
a non-zero exit code, for usability all values should be compared (and all
mismatches reported) before exiting.

The "target" can be one of a: a repository, a workflow, or a job. If the target
is a repository, all actions used in all jobs in all workflows in the repository
will be considered. If the target is a workflow, only actions used in all jobs
in the workflow will be considered. If the target is a job, only actions used in
the job will be considered.

Redundant checksums are ignored by this process.

## Procedures
Expand Down
36 changes: 27 additions & 9 deletions cmd/ghasum/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,10 @@ func cmdVerify(argv []string) error {
return err
}

c, err := cache.New(*flagCache, *flagNoCache)
if err != nil {
return errors.Join(errCache, err)
var job string
if i := strings.LastIndexByte(target, 0x3A); i >= 0 {
job = target[i+1:]
target = target[0:i]
}

stat, err := os.Stat(target)
Expand All @@ -66,10 +67,16 @@ func cmdVerify(argv []string) error {
target = repo
}

c, err := cache.New(*flagCache, *flagNoCache)
if err != nil {
return errors.Join(errCache, err)
}

cfg := ghasum.Config{
Repo: os.DirFS(target),
Path: target,
Workflow: workflow,
Job: job,
Cache: c,
}

Expand Down Expand Up @@ -97,15 +104,26 @@ func helpVerify() string {

Verify the Actions in the target against the stored checksums. If no target is
provided it will default to the current working directory. If the checksums do
not match this command will error with a non-zero exit code.
not match this command will error with a non-zero exit code. If ghasum is not
yet initialized this command errors (see "ghasum help init").

The target can be either a directory or a file. If it is a directory it must be
the root of a repository (that is, it should contain the .github directory). In
this case checksums will be verified for every workflow in the repository. If it
is a file it must be a workflow file in a repository. In this case checksums
will be verified only for the given workflow.
the root of a repository (that is, it should contain the .github directory). For
example:

ghasum verify my-project

In this case checksums will be verified for every workflow in the repository. If
it is a file it must be a workflow file in a repository. For example:

ghasum verify my-project/.github/workflows/workflow.yml

In this case checksums will be verified for all jobs in the given workflow. If
it is a file it may specify a job by using a ":job" suffix. For example:

ghasum verify my-project/.github/workflows/workflow.yml:job-key

If ghasum is not yet initialized this command errors (see "ghasum help init").
In this case checksums will be verified only for the given job in the workflow.

The available flags are:

Expand Down
15 changes: 12 additions & 3 deletions internal/gha/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,11 @@ func workflowsInRepo(repo fs.FS) ([][]byte, error) {
return nil
}

file, err := repo.Open(entryPath)
data, err := workflowInRepo(repo, entryPath)
if err != nil {
return fmt.Errorf("could not open workflow at %q: %v", entryPath, err)
return err
}

data, _ := io.ReadAll(file)
workflows = append(workflows, data)
return nil
}
Expand All @@ -87,3 +86,13 @@ func workflowsInRepo(repo fs.FS) ([][]byte, error) {

return workflows, nil
}

func workflowInRepo(repo fs.FS, path string) ([]byte, error) {
file, err := repo.Open(path)
if err != nil {
return nil, fmt.Errorf("could not open workflow at %q: %v", path, err)
}

data, _ := io.ReadAll(file)
return data, nil
}
44 changes: 41 additions & 3 deletions internal/gha/gha.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package gha

import (
"fmt"
"io/fs"
"path"
)
Expand Down Expand Up @@ -63,13 +64,50 @@ func RepoActions(repo fs.FS) ([]GitHubAction, error) {
return actions, nil
}

// WorkflowActions extracts the GitHub Actions used in the provided workflow.
func WorkflowActions(rawWorkflow []byte) ([]GitHubAction, error) {
w, err := parseWorkflow(rawWorkflow)
// WorkflowActions extracts the GitHub Actions used in the specified workflow at
// the given file system hierarchy.
func WorkflowActions(repo fs.FS, path string) ([]GitHubAction, error) {
data, err := workflowInRepo(repo, path)
if err != nil {
return nil, err
}

w, err := parseWorkflow(data)
if err != nil {
return nil, err
}

actions, err := actionsInWorkflows([]workflow{w})
if err != nil {
return nil, err
}

return actions, nil
}

// JobActions extracts the GitHub Actions used in the specified job in the
// specified workflow at the given file system hierarchy.
func JobActions(repo fs.FS, path, name string) ([]GitHubAction, error) {
data, err := workflowInRepo(repo, path)
if err != nil {
return nil, err
}

w, err := parseWorkflow(data)
if err != nil {
return nil, err
}

for job := range w.Jobs {
if job != name {
delete(w.Jobs, job)
}
}

if len(w.Jobs) == 0 {
return nil, fmt.Errorf("job %q not found in workflow %q", name, path)
}

actions, err := actionsInWorkflows([]workflow{w})
if err != nil {
return nil, err
Expand Down
Loading