diff --git a/.github/workflows/keyless-salsa-integration.yaml b/.github/workflows/keyless-salsa-integration.yaml index 89fe1b4..ea5ee74 100644 --- a/.github/workflows/keyless-salsa-integration.yaml +++ b/.github/workflows/keyless-salsa-integration.yaml @@ -35,7 +35,7 @@ jobs: context: integration-test push: true tags: ${{ env.IMAGE }} - - name: Generate provenance, upload and sign image + - name: Generate provenance, sign and upload image uses: ./ with: identity_token: ${{ steps.google.outputs.id_token }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0b9519f..ad233be 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,7 +10,7 @@ on: - 'Makefile' env: - VERSION: v0.4 + VERSION: v0.5 IMAGE_NAME: ghcr.io/${{ github.repository }} COSIGN_VERSION: v1.13.1 SYFT_VERSION: v0.44.1 diff --git a/.github/workflows/service-account-salsa-integration.yml b/.github/workflows/service-account-salsa-integration.yml index ceec11b..fa750b9 100644 --- a/.github/workflows/service-account-salsa-integration.yml +++ b/.github/workflows/service-account-salsa-integration.yml @@ -34,7 +34,7 @@ jobs: push: true tags: ${{ env.IMAGE }} # Added to a workflow - - name: Generate provenance, upload and sign image + - name: Generate provenance, sign and upload image id: salsa # nais/salsa@v... uses: ./ diff --git a/README.md b/README.md index 8721bc9..24d7cd0 100644 --- a/README.md +++ b/README.md @@ -189,7 +189,7 @@ jobs: credentials_json: ${{ secrets.GCP_CREDENTIALS }} - name: Provenance, upload and sign attestation - uses: nais/salsa@v0.3 + uses: nais/salsa@v0.x with: key: ${{ env.KEY }} docker_pwd: ${{ secrets.GITHUB_TOKEN }} @@ -260,8 +260,8 @@ jobs: id_token_audience: sigstore id_token_include_email: true - - name: Generate provenance, upload and sign image - uses: nais/salsa@v0.3 + - name: Generate provenance, sign and upload image + uses: nais/salsa@v0.x with: identity_token: ${{ steps.google.outputs.id_token }} docker_pwd: ${{ secrets.GITHUB_TOKEN }} @@ -306,8 +306,8 @@ store the cosign signatures and attestations, see more specification in the [cosign docs](https://github.com/sigstore/cosign#specifying-registry) ```yaml -- name: Generate provenance, upload and sign image - uses: nais/salsa@v0.3 +- name: Generate provenance, sign and upload image + uses: nais/salsa@v0.x with: key: ${{ secrets.SALSA_KMS_KEY }} docker_pwd: ${{ secrets.GITHUB_TOKEN }} @@ -331,13 +331,33 @@ build tool can authenticate with a `PAT`. Use the `with.github_token` field to a `with.token_key_pattern` can be used to specify a key pattern, other than default `GITHUB_TOKEN`. +#### Maven Options + +`with.mvn_opts` - (optional) additional maven options in a comma-delimited string. + +Useful when your project depends on a custom maven settings file or use dependencies from a private repository. + +Actor need to set `with.github_token` with access to the private repository. + +```yaml + - name: Generate provenance, sign and upload image + uses: nais/salsa@v0.x + with: + mvn_opts: "-s ./.mvn/settings.xml, -Dmaven.repo.local=/path/to/local/repo" + github_token: ${{ secrets.PAT }} +``` + #### GitHub context -The GitHub context contains information about the workflow run and the event that triggered the run. By default, this +`with.github_context` - (required) default to `true` to include the github context in the provenance. + +The github context contains information about the workflow run and the event that triggered the run. By default, this action uses the [GitHub context](https://docs.github.com/en/actions/learn-github-actions/contexts#github-context). #### Runner Context +`with.runner_context` - (required) default to `true` to include the runner context in the provenance. + The runner context contains information about the runner that is executing the current job. By default, this action uses the [Runner context](https://docs.github.com/en/actions/learn-github-actions/contexts#runner-context). @@ -356,6 +376,7 @@ The Following inputs can be used as `step.with` keys | `github_token` | String | "" | Token to authenticate and read private packages, the token must have read:packages scope | False | | `token_key_pattern` | String | "" | If a token is provided but the the key pattern is different from the default key pattern "GITHUB_TOKEN" | False | | `build_started_on` | String | "" | Specify a workflow build start time. Default is set to github_context `event.head_commit` or `event.workflow_run.head_commit` depending on workflow usage | False | +| `mvn_opts` | String | "" | A comma-delimited string with additional maven cli options for the dependence build | False | | `repo_dir` | String | $GITHUB_WORKSPACE | **Internal value (do not set):** Root of directory to look for build files | False | | `github_context` | String | ${{ toJSON(github) }} | **Internal value (do not set):** the [github context](#github-context) object in json | False | | `runner_context` | String | ${{ toJSON(runner) }} | **Internal value (do not set):** the [runner context](#runner-context) object in json | False | diff --git a/action.yml b/action.yml index 4ed9de1..3e89818 100644 --- a/action.yml +++ b/action.yml @@ -68,9 +68,15 @@ inputs: required: false default: "" + mvn_opts: + description: |- + A comma-delimited string with additional maven options. + required: false + default: "" + github_token: description: |- - Token to authenticate private packages repository. + PAT with at least packages:read scope to install packages associated with other private repositories (which GITHUB_TOKEN can't access). required: false default: "" @@ -97,7 +103,7 @@ inputs: runs: using: 'docker' - image: 'docker://ghcr.io/nais/salsa:v0.4' + image: 'docker://ghcr.io/nais/salsa:v0.5' args: - ${{ inputs.repo_dir }} - ${{ inputs.repo_name }} @@ -108,6 +114,8 @@ runs: - ${{ inputs.repo_sub_dir }} - ${{ inputs.key }} - ${{ inputs.identity_token }} + - ${{ inputs.mvn_opts }} + - ${{ inputs.github_token }} - ${{ inputs.docker_user }} - ${{ inputs.github_token }} - ${{ inputs.token_key_pattern }} diff --git a/entrypoint.sh b/entrypoint.sh index ff2c6c2..ca63729 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -86,6 +86,7 @@ scan() { --env-context "$ENVS" \ --subDir "$INPUT_REPO_SUB_DIR" \ --with-deps="$INPUT_DEPENDENCIES" \ + --mvn-opts "$INPUT_MVN_OPTS" \ --build-started-on "$INPUT_BUILD_STARTED_ON" \ --remote-run } diff --git a/pkg/build/jvm/gradle_test.go b/pkg/build/jvm/gradle_test.go index 808f65e..88dcc8b 100644 --- a/pkg/build/jvm/gradle_test.go +++ b/pkg/build/jvm/gradle_test.go @@ -76,7 +76,7 @@ func TestBuildGradle(t *testing.T) { }, { Name: "cant find supported build type", - BuildType: BuildMaven(), + BuildType: BuildMaven(""), WorkDir: "testdata/jvm/gradle-kts", Error: true, ErrorMessage: "no supported build files found: testdata/jvm/gradle-kts", diff --git a/pkg/build/jvm/mvn.go b/pkg/build/jvm/mvn.go index 15496fc..2537860 100644 --- a/pkg/build/jvm/mvn.go +++ b/pkg/build/jvm/mvn.go @@ -17,12 +17,16 @@ const mavenBuildFileName = "pom.xml" type Maven struct { BuildFilePatterns []string + CmdOptions string } -func BuildMaven() build.Tool { - return &Maven{ +func BuildMaven(cmdOpts string) build.Tool { + m := &Maven{ BuildFilePatterns: []string{mavenBuildFileName}, + CmdOptions: cmdOpts, } + + return m } func (m Maven) BuildFiles() []string { @@ -33,11 +37,8 @@ func (m Maven) ResolveDeps(workDir string) (*build.ArtifactDependencies, error) cmd := utils.NewCmd( "mvn", "dependency:copy-dependencies", - []string{ - "-DincludeScope=runtime", - "-Dmdep.useRepositoryLayout=true", - }, - nil, + defaultMavenOpts(), + m.parsedCmdOpts(), workDir, ) @@ -55,9 +56,30 @@ func (m Maven) ResolveDeps(workDir string) (*build.ArtifactDependencies, error) args = append(args, cmd.Name) args = append(args, cmd.SubCmd) args = append(args, cmd.Flags...) + args = append(args, cmd.Args...) return build.ArtifactDependency(deps, cmd.Name, strings.Join(args, " ")), nil } +func defaultMavenOpts() []string { + return []string{ + "-DincludeScope=runtime", + "-Dmdep.useRepositoryLayout=true", + } +} + +func (m Maven) parsedCmdOpts() []string { + if m.CmdOptions == "" { + return nil + } + + parsed := strings.Split(m.CmdOptions, ",") + for i, s := range parsed { + parsed[i] = strings.TrimSpace(s) + } + + return parsed +} + func MavenCompileAndRuntimeTimeDeps(rootPath string) (map[string]build.Dependency, error) { files, err := findJarFiles(rootPath) if err != nil { diff --git a/pkg/build/jvm/mvn_test.go b/pkg/build/jvm/mvn_test.go index 7ea41e5..a0b3259 100644 --- a/pkg/build/jvm/mvn_test.go +++ b/pkg/build/jvm/mvn_test.go @@ -30,7 +30,7 @@ func TestBuildMaven(t *testing.T) { tests := []test.IntegrationTest{ { Name: "find build file and parse output", - BuildType: BuildMaven(), + BuildType: BuildMaven(""), WorkDir: "testdata/jvm/maven", BuildPath: "/usr/local/bin/mvn", Cmd: "mvn dependency:copy-dependencies -DincludeScope=runtime -Dmdep.useRepositoryLayout=true", @@ -43,11 +43,24 @@ func TestBuildMaven(t *testing.T) { }, { Name: "cant find build file", - BuildType: BuildMaven(), + BuildType: BuildMaven(""), WorkDir: "testdata/whatever", Error: true, ErrorMessage: "could not find match, reading dir open testdata/whatever: no such file or directory", }, + { + Name: "Add additional command line arguments as a part of the mvn command", + BuildType: BuildMaven("-q, -am, -X,-B, -D yolo=molo"), + Cmd: "mvn dependency:copy-dependencies -DincludeScope=runtime -Dmdep.useRepositoryLayout=true -q -am -X -B -D yolo=molo", + WorkDir: "testdata/jvm/maven", + BuildPath: "/usr/local/bin/mvn", + Want: test.Want{ + Key: "com.google.code.gson:gson", + Version: "2.8.6", + Algo: "sha256", + Digest: "c8fb4839054d280b3033f800d1f5a97de2f028eb8ba2eb458ad287e536f3f25f", + }, + }, } test.Run(t, tests) diff --git a/pkg/build/jvm/testdata/jvm/gradle/.gradle/7.5.1/checksums/checksums.lock b/pkg/build/jvm/testdata/jvm/gradle/.gradle/7.5.1/checksums/checksums.lock deleted file mode 100644 index 402456c..0000000 Binary files a/pkg/build/jvm/testdata/jvm/gradle/.gradle/7.5.1/checksums/checksums.lock and /dev/null differ diff --git a/pkg/build/jvm/testdata/jvm/gradle/.gradle/7.5.1/checksums/md5-checksums.bin b/pkg/build/jvm/testdata/jvm/gradle/.gradle/7.5.1/checksums/md5-checksums.bin deleted file mode 100644 index 38d8bef..0000000 Binary files a/pkg/build/jvm/testdata/jvm/gradle/.gradle/7.5.1/checksums/md5-checksums.bin and /dev/null differ diff --git a/pkg/build/jvm/testdata/jvm/gradle/.gradle/7.5.1/checksums/sha1-checksums.bin b/pkg/build/jvm/testdata/jvm/gradle/.gradle/7.5.1/checksums/sha1-checksums.bin deleted file mode 100644 index 8471840..0000000 Binary files a/pkg/build/jvm/testdata/jvm/gradle/.gradle/7.5.1/checksums/sha1-checksums.bin and /dev/null differ diff --git a/pkg/build/jvm/testdata/jvm/gradle/.gradle/7.5.1/checksums/sha256-checksums.bin b/pkg/build/jvm/testdata/jvm/gradle/.gradle/7.5.1/checksums/sha256-checksums.bin deleted file mode 100644 index 64943bd..0000000 Binary files a/pkg/build/jvm/testdata/jvm/gradle/.gradle/7.5.1/checksums/sha256-checksums.bin and /dev/null differ diff --git a/pkg/build/test/buildtool_test_util.go b/pkg/build/test/buildtool_test_util.go index 6fa7e27..e341afa 100644 --- a/pkg/build/test/buildtool_test_util.go +++ b/pkg/build/test/buildtool_test_util.go @@ -48,7 +48,7 @@ func (in IntegrationTest) integrationTest(t *testing.T) { } else { assert.NoError(t, err) assert.NotNil(t, deps) - assert.Equal(t, in.Cmd, deps.Cmd.CmdFlags) + assert.Equal(t, in.Cmd, deps.CmdFlags()) assert.NotEmpty(t, deps) assert.Equal(t, expected[in.Want.Key], deps.RuntimeDeps[in.Want.Key]) } diff --git a/pkg/commands/scan.go b/pkg/commands/scan.go index be6b57b..975c295 100644 --- a/pkg/commands/scan.go +++ b/pkg/commands/scan.go @@ -21,6 +21,7 @@ import ( var ( buildContext string runnerContext string + mvnOpts string envContext string Config *ProvenanceConfig ) @@ -50,7 +51,7 @@ var scanCmd = &cobra.Command{ deps := &build.ArtifactDependencies{} if Config.WithDependencies { - generatedDeps, err := InitBuildTools().DetectDeps(workDir) + generatedDeps, err := InitBuildTools(mvnOpts).DetectDeps(workDir) if err != nil { return fmt.Errorf("detecting dependecies: %v", err) } @@ -98,11 +99,11 @@ func GenerateProvenance(scanCfg *config.ScanConfiguration) error { return nil } -func InitBuildTools() build.Tools { +func InitBuildTools(mavenOpts string) build.Tools { return build.Tools{ Tools: []build.Tool{ jvm.BuildGradle(), - jvm.BuildMaven(), + jvm.BuildMaven(mavenOpts), golang.BuildGo(), nodejs.BuildNpm(), nodejs.BuildYarn(), @@ -118,5 +119,6 @@ func init() { scanCmd.Flags().StringVar(&runnerContext, "runner-context", "", "context of runner") scanCmd.Flags().StringVar(&envContext, "env-context", "", "environmental variables of current context") scanCmd.Flags().BoolVar(&Config.WithDependencies, "with-deps", true, "specify if the cli should generate dependencies for a provenance") + scanCmd.Flags().StringVar(&mvnOpts, "mvn-opts", "", "pass additional Comma-delimited list of options to the maven build tool") scanCmd.Flags().StringVar(&Config.BuildStartedOn, "build-started-on", "", "set start time for the build") }