diff --git a/internal/app/cmd/testdata/validate/bad-directory/.forgejo/workflows/workflow1.yml b/internal/app/cmd/testdata/validate/bad-directory/.forgejo/workflows/workflow1.yml new file mode 100644 index 00000000..802c1a24 --- /dev/null +++ b/internal/app/cmd/testdata/validate/bad-directory/.forgejo/workflows/workflow1.yml @@ -0,0 +1,6 @@ +on: [push] +jobs: + test: + ruins-on: docker + steps: + - run: echo All good! diff --git a/internal/app/cmd/testdata/validate/bad-directory/action.yml b/internal/app/cmd/testdata/validate/bad-directory/action.yml new file mode 100644 index 00000000..6f70f275 --- /dev/null +++ b/internal/app/cmd/testdata/validate/bad-directory/action.yml @@ -0,0 +1,67 @@ +name: 'Forgejo release download and upload' +author: 'Forgejo authors' +description: | + Upload or download the assets of a release to a Forgejo instance. +inputs: + badinput: scalarinsteadofmap + url: + description: 'URL of the Forgejo instance' + default: '${{ env.FORGEJO_SERVER_URL }}' + repo: + description: 'owner/project relative to the URL' + default: '${{ forge.repository }}' + tag: + description: 'Tag of the release' + default: '${{ forge.ref_name }}' + title: + description: 'Title of the release (defaults to tag)' + sha: + description: 'SHA of the release' + default: '${{ forge.sha }}' + token: + description: 'Forgejo application token' + default: '${{ forge.token }}' + release-dir: + description: 'Directory in whichs release assets are uploaded or downloaded' + required: true + release-notes: + description: 'Release notes' + direction: + description: 'Can either be `download` or `upload`' + required: true + gpg-private-key: + description: 'GPG Private Key to sign the release artifacts' + gpg-passphrase: + description: 'Passphrase of the GPG Private Key' + download-retry: + description: 'Number of times to retry if the release is not ready (default 1)' + download-latest: + description: 'Download the latest release' + default: false + verbose: + description: 'Increase the verbosity level' + default: false + override: + description: 'Override an existing release by the same `{tag}`' + default: false + prerelease: + description: 'Mark Release as Pre-Release' + default: false + release-notes-assistant: + description: 'Generate release notes with Release Notes Assistant' + default: false + hide-archive-link: + description: 'Hide the archive links' + default: false + +runs: + using: "composite" + steps: + - if: ${{ inputs.release-notes-assistant }} + uses: https://data.forgejo.org/actions/cache@v4 + with: + key: rna-${{ inputs.repo }} + path: ${{ forge.action_path }}/rna + + - run: echo "${{ forge.action_path }}" >> $FORGEJO_PATH + shell: bash diff --git a/internal/app/cmd/testdata/validate/good-directory/.forgejo/workflows/action.yml b/internal/app/cmd/testdata/validate/good-directory/.forgejo/workflows/action.yml new file mode 100644 index 00000000..efdc13fc --- /dev/null +++ b/internal/app/cmd/testdata/validate/good-directory/.forgejo/workflows/action.yml @@ -0,0 +1,6 @@ +on: [push] +jobs: + test: + runs-on: docker + steps: + - run: echo All good! diff --git a/internal/app/cmd/testdata/validate/good-directory/.forgejo/workflows/workflow1.yml b/internal/app/cmd/testdata/validate/good-directory/.forgejo/workflows/workflow1.yml new file mode 100644 index 00000000..efdc13fc --- /dev/null +++ b/internal/app/cmd/testdata/validate/good-directory/.forgejo/workflows/workflow1.yml @@ -0,0 +1,6 @@ +on: [push] +jobs: + test: + runs-on: docker + steps: + - run: echo All good! diff --git a/internal/app/cmd/testdata/validate/good-directory/.forgejo/workflows/workflow2.yaml b/internal/app/cmd/testdata/validate/good-directory/.forgejo/workflows/workflow2.yaml new file mode 100644 index 00000000..efdc13fc --- /dev/null +++ b/internal/app/cmd/testdata/validate/good-directory/.forgejo/workflows/workflow2.yaml @@ -0,0 +1,6 @@ +on: [push] +jobs: + test: + runs-on: docker + steps: + - run: echo All good! diff --git a/internal/app/cmd/testdata/validate/good-directory/.gitea/workflows/bad.yml b/internal/app/cmd/testdata/validate/good-directory/.gitea/workflows/bad.yml new file mode 100644 index 00000000..802c1a24 --- /dev/null +++ b/internal/app/cmd/testdata/validate/good-directory/.gitea/workflows/bad.yml @@ -0,0 +1,6 @@ +on: [push] +jobs: + test: + ruins-on: docker + steps: + - run: echo All good! diff --git a/internal/app/cmd/testdata/validate/good-directory/.github/workflows/bad.yml b/internal/app/cmd/testdata/validate/good-directory/.github/workflows/bad.yml new file mode 100644 index 00000000..802c1a24 --- /dev/null +++ b/internal/app/cmd/testdata/validate/good-directory/.github/workflows/bad.yml @@ -0,0 +1,6 @@ +on: [push] +jobs: + test: + ruins-on: docker + steps: + - run: echo All good! diff --git a/internal/app/cmd/testdata/validate/good-directory/action.yml b/internal/app/cmd/testdata/validate/good-directory/action.yml new file mode 100644 index 00000000..cb66d230 --- /dev/null +++ b/internal/app/cmd/testdata/validate/good-directory/action.yml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: MIT +name: 'Forgejo release download and upload' +author: 'Forgejo authors' +description: | + Upload or download the assets of a release to a Forgejo instance. +inputs: + url: + description: 'URL of the Forgejo instance' + default: '${{ env.FORGEJO_SERVER_URL }}' + repo: + description: 'owner/project relative to the URL' + default: '${{ forge.repository }}' + tag: + description: 'Tag of the release' + default: '${{ forge.ref_name }}' + title: + description: 'Title of the release (defaults to tag)' + sha: + description: 'SHA of the release' + default: '${{ forge.sha }}' + token: + description: 'Forgejo application token' + default: '${{ forge.token }}' + release-dir: + description: 'Directory in whichs release assets are uploaded or downloaded' + required: true + release-notes: + description: 'Release notes' + direction: + description: 'Can either be `download` or `upload`' + required: true + gpg-private-key: + description: 'GPG Private Key to sign the release artifacts' + gpg-passphrase: + description: 'Passphrase of the GPG Private Key' + download-retry: + description: 'Number of times to retry if the release is not ready (default 1)' + download-latest: + description: 'Download the latest release' + default: false + verbose: + description: 'Increase the verbosity level' + default: false + override: + description: 'Override an existing release by the same `{tag}`' + default: false + prerelease: + description: 'Mark Release as Pre-Release' + default: false + release-notes-assistant: + description: 'Generate release notes with Release Notes Assistant' + default: false + hide-archive-link: + description: 'Hide the archive links' + default: false + +runs: + using: "composite" + steps: + - if: ${{ inputs.release-notes-assistant }} + uses: https://data.forgejo.org/actions/cache@v4 + with: + key: rna-${{ inputs.repo }} + path: ${{ forge.action_path }}/rna + + - run: echo "${{ forge.action_path }}" >> $FORGEJO_PATH + shell: bash diff --git a/internal/app/cmd/testdata/validate/good-directory/subaction/action.yaml b/internal/app/cmd/testdata/validate/good-directory/subaction/action.yaml new file mode 100644 index 00000000..cb66d230 --- /dev/null +++ b/internal/app/cmd/testdata/validate/good-directory/subaction/action.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: MIT +name: 'Forgejo release download and upload' +author: 'Forgejo authors' +description: | + Upload or download the assets of a release to a Forgejo instance. +inputs: + url: + description: 'URL of the Forgejo instance' + default: '${{ env.FORGEJO_SERVER_URL }}' + repo: + description: 'owner/project relative to the URL' + default: '${{ forge.repository }}' + tag: + description: 'Tag of the release' + default: '${{ forge.ref_name }}' + title: + description: 'Title of the release (defaults to tag)' + sha: + description: 'SHA of the release' + default: '${{ forge.sha }}' + token: + description: 'Forgejo application token' + default: '${{ forge.token }}' + release-dir: + description: 'Directory in whichs release assets are uploaded or downloaded' + required: true + release-notes: + description: 'Release notes' + direction: + description: 'Can either be `download` or `upload`' + required: true + gpg-private-key: + description: 'GPG Private Key to sign the release artifacts' + gpg-passphrase: + description: 'Passphrase of the GPG Private Key' + download-retry: + description: 'Number of times to retry if the release is not ready (default 1)' + download-latest: + description: 'Download the latest release' + default: false + verbose: + description: 'Increase the verbosity level' + default: false + override: + description: 'Override an existing release by the same `{tag}`' + default: false + prerelease: + description: 'Mark Release as Pre-Release' + default: false + release-notes-assistant: + description: 'Generate release notes with Release Notes Assistant' + default: false + hide-archive-link: + description: 'Hide the archive links' + default: false + +runs: + using: "composite" + steps: + - if: ${{ inputs.release-notes-assistant }} + uses: https://data.forgejo.org/actions/cache@v4 + with: + key: rna-${{ inputs.repo }} + path: ${{ forge.action_path }}/rna + + - run: echo "${{ forge.action_path }}" >> $FORGEJO_PATH + shell: bash diff --git a/internal/app/cmd/testdata/validate/make-repositories.sh b/internal/app/cmd/testdata/validate/make-repositories.sh index 6f3918ee..0047cb4c 100755 --- a/internal/app/cmd/testdata/validate/make-repositories.sh +++ b/internal/app/cmd/testdata/validate/make-repositories.sh @@ -35,6 +35,10 @@ git clone --bare $tmpdir/good good-repository rm -fr good-repository/hooks touch good-repository/refs/placeholder +rm -fr good-directory +git clone $tmpdir/good good-directory +rm -fr good-directory/.git + # bad mkdir $tmpdir/bad @@ -54,3 +58,7 @@ rm -fr bad-repository git clone --bare $tmpdir/bad bad-repository rm -fr bad-repository/hooks touch bad-repository/refs/placeholder + +rm -fr bad-directory +git clone $tmpdir/bad bad-directory +rm -fr bad-directory/.git diff --git a/internal/app/cmd/validate.go b/internal/app/cmd/validate.go index ca5ab732..474ff56f 100644 --- a/internal/app/cmd/validate.go +++ b/internal/app/cmd/validate.go @@ -23,6 +23,7 @@ type validateArgs struct { path string repository string clonedir string + directory string workflow bool action bool } @@ -145,8 +146,16 @@ func validateRepository(validateArgs *validateArgs) error { return nil } +func processDirectory(validateArgs *validateArgs) { + if len(validateArgs.directory) > 0 { + validateArgs.repository = validateArgs.directory + validateArgs.clonedir = validateArgs.directory + } +} + func runValidate(_ context.Context, validateArgs *validateArgs) func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error { + processDirectory(validateArgs) if len(validateArgs.path) > 0 { return validatePath(validateArgs) } else if len(validateArgs.repository) > 0 { @@ -168,9 +177,15 @@ Validate workflows or actions with a schema verifying they are conformant. The --path argument is a filename that will be validated as a workflow (if the --workflow flag is set) or as an action (if the --action flag is set). -The --repository argument is a URL to a Git repository. It will be -cloned (in the --clonedir directory or a temporary location removed -when the validation completes). The following files will be validated: +The --repository argument is a URL to a Git repository that contains +workflows or actions. It will be cloned (in the --clonedir directory +or a temporary location removed when the validation completes). + +The --directory argument is the path a repository to be explored for +files to validate. + +The following files will be validated when exploring the clone of a repository +(--repository) or a directory (--directory): - All .forgejo/workflows/*.{yml,yaml} files as workflows - All **/action.{yml,yaml} files as actions @@ -185,9 +200,11 @@ when the validation completes). The following files will be validated: validateCmd.Flags().StringVar(&validateArgs.clonedir, "clonedir", "", "directory in which the repository will be cloned") validateCmd.Flags().StringVar(&validateArgs.repository, "repository", "", "URL to a repository to validate") + validateCmd.Flags().StringVar(&validateArgs.directory, "directory", "", "directory to a repository to validate") validateCmd.Flags().StringVar(&validateArgs.path, "path", "", "path to the file") - validateCmd.MarkFlagsOneRequired("repository", "path") - validateCmd.MarkFlagsMutuallyExclusive("repository", "path") + validateCmd.MarkFlagsOneRequired("repository", "path", "directory") + validateCmd.MarkFlagsMutuallyExclusive("repository", "path", "directory") + validateCmd.MarkFlagsMutuallyExclusive("directory", "clonedir") return validateCmd } diff --git a/internal/app/cmd/validate_test.go b/internal/app/cmd/validate_test.go index 7442d1d2..8a7c6c71 100644 --- a/internal/app/cmd/validate_test.go +++ b/internal/app/cmd/validate_test.go @@ -27,10 +27,20 @@ func Test_validateCmd(t *testing.T) { message: "one of --workflow or --action must be set", }, { - name: "MutuallyExclusive", + name: "MutuallyExclusiveActionWorkflow", args: []string{"--action", "--workflow", "--path", "/tmp"}, message: "[action workflow] were all set", }, + { + name: "MutuallyExclusiveRepositoryDirectory", + args: []string{"--repository", "example.com", "--directory", "."}, + message: "[directory repository] were all set", + }, + { + name: "MutuallyExclusiveClonedirDirectory", + args: []string{"--clonedir", ".", "--directory", "."}, + message: "[clonedir directory] were all set", + }, { name: "PathActionOK", args: []string{"--action", "--path", "testdata/validate/good-action.yml"}, @@ -51,6 +61,21 @@ func Test_validateCmd(t *testing.T) { args: []string{"--workflow", "--path", "testdata/validate/bad-workflow.yml"}, stdOut: "Unknown Property ruins-on", }, + { + name: "DirectoryOK", + args: []string{"--directory", "testdata/validate/good-directory"}, + stdOut: "action.yml action schema validation OK\nsubaction/action.yaml action schema validation OK\n.forgejo/workflows/action.yml workflow schema validation OK\n.forgejo/workflows/workflow1.yml workflow schema validation OK\n.forgejo/workflows/workflow2.yaml workflow schema validation OK", + }, + { + name: "DirectoryActionNOK", + args: []string{"--directory", "testdata/validate/bad-directory"}, + stdOut: "action.yml action schema validation failed", + }, + { + name: "DirectoryWorkflowNOK", + args: []string{"--directory", "testdata/validate/bad-directory"}, + stdOut: ".forgejo/workflows/workflow1.yml workflow schema validation failed", + }, { name: "RepositoryOK", args: []string{"--repository", "testdata/validate/good-repository"},