diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 000000000..8d0b9f879 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,32 @@ +# Copyright The ORAS Authors. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: "Close stale issues and PRs" +on: + schedule: + - cron: "30 1 * * *" + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v8 + with: + stale-issue-message: "This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 30 days." + stale-pr-message: "This PR is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 30 days." + close-issue-message: "This issue was closed because it has been stalled for 30 days with no activity." + close-pr-message: "This PR was closed because it has been stalled for 30 days with no activity." + days-before-issue-stale: 60 + days-before-pr-stale: 45 + days-before-issue-close: 30 + days-before-pr-close: 30 diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml new file mode 100644 index 000000000..fe2f952c1 --- /dev/null +++ b/.github/workflows/golangci-lint.yml @@ -0,0 +1,41 @@ +# Copyright The ORAS Authors. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: golangci-lint + +on: + pull_request: + paths-ignore: + - 'docs/**' + +permissions: + contents: read + +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + strategy: + matrix: + go-version: ['1.20'] + fail-fast: true + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup Go ${{ matrix.go-version }} + uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.go-version }} + check-latest: true + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 diff --git a/.github/workflows/release-ghcr.yml b/.github/workflows/release-ghcr.yml index d9ba9575b..e39ae4ae4 100644 --- a/.github/workflows/release-ghcr.yml +++ b/.github/workflows/release-ghcr.yml @@ -35,8 +35,7 @@ jobs: if [[ "${VERSION}" == "${BRANCH_NAME}" ]]; then VERSION=$(git rev-parse --short HEAD) fi - echo ::set-output name=version::${VERSION} - echo ::set-output name=ref::ghcr.io/${{ github.repository }}:${VERSION} + echo "ref=ghcr.io/${{ github.repository }}:${VERSION}" >> $GITHUB_OUTPUT - name: docker login uses: docker/login-action@v2 with: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 95d6182fe..f0bcf8e35 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,3 @@ # Contributing -Please refer to the [ORAS Contributing guide](https://oras.land/docs/contributing). +Please refer to the [ORAS Contributing guide](https://oras.land/docs/community/contributing_guide). diff --git a/Makefile b/Makefile index cbf8181c4..df548f223 100644 --- a/Makefile +++ b/Makefile @@ -134,8 +134,7 @@ teste2e-covdata: ## test e2e coverage export GOCOVERDIR=$(CURDIR)/test/e2e/.cover; \ rm -rf $$GOCOVERDIR; \ mkdir -p $$GOCOVERDIR; \ - $(MAKE) teste2e; \ - $(GO_EXE) tool covdata textfmt -i=$$GOCOVERDIR -o "$(CURDIR)/test/e2e/coverage.txt" + $(MAKE) teste2e && $(GO_EXE) tool covdata textfmt -i=$$GOCOVERDIR -o "$(CURDIR)/test/e2e/coverage.txt" .PHONY: help help: ## Display this help diff --git a/README.md b/README.md index cf9831a3f..8059c4785 100644 --- a/README.md +++ b/README.md @@ -11,11 +11,11 @@ ## Docs Documentation for the ORAS CLI is located on -the project website: [oras.land/cli](https://oras.land/docs/category/cli) +the project website: [oras.land/cli](https://oras.land/docs/category/oras-commands) ## Development Environment Setup -Refer to the [development guide](https://oras.land/docs/CLI/developer_guide) to get started [contributing to ORAS](https://oras.land/docs/contributing). +Refer to the [development guide](https://oras.land/docs/community/developer_guide) to get started [contributing to ORAS](https://oras.land/docs/community/contributing_guide). ## Code of Conduct diff --git a/cmd/oras/internal/display/print.go b/cmd/oras/internal/display/print.go index 35de4ec5a..48374a327 100644 --- a/cmd/oras/internal/display/print.go +++ b/cmd/oras/internal/display/print.go @@ -116,7 +116,7 @@ func (p *tagManifestStatusForRepo) PushReference(ctx context.Context, expected o if p.printHint != nil { p.printHint.Do(func() { ref := p.refPrefix + "@" + expected.Digest.String() - Print("Tagging", ref) + _ = Print("Tagging", ref) }) } if err := p.Repository.PushReference(ctx, expected, content, reference); err != nil { @@ -136,9 +136,10 @@ func (p *tagManifestStatusForTarget) Tag(ctx context.Context, desc ocispec.Descr if p.printHint != nil { p.printHint.Do(func() { ref := p.refPrefix + "@" + desc.Digest.String() - Print("Tagging", ref) + _ = Print("Tagging", ref) }) } + if err := p.Target.Tag(ctx, desc, reference); err != nil { return err } diff --git a/cmd/oras/internal/option/applier.go b/cmd/oras/internal/option/applier.go index f47aba14b..1d177e96e 100644 --- a/cmd/oras/internal/option/applier.go +++ b/cmd/oras/internal/option/applier.go @@ -29,7 +29,7 @@ type FlagApplier interface { // NOTE: The option argument need to be a pointer to the options, so its value // becomes addressable. func ApplyFlags(optsPtr interface{}, target *pflag.FlagSet) { - rangeFields(optsPtr, func(fa FlagApplier) error { + _ = rangeFields(optsPtr, func(fa FlagApplier) error { fa.ApplyFlags(target) return nil }) diff --git a/cmd/oras/internal/option/packer_test.go b/cmd/oras/internal/option/packer_test.go index 5c7470ee5..e0cd51339 100644 --- a/cmd/oras/internal/option/packer_test.go +++ b/cmd/oras/internal/option/packer_test.go @@ -70,7 +70,10 @@ func TestPacker_LoadManifestAnnotations_err(t *testing.T) { func TestPacker_LoadManifestAnnotations_annotationFile(t *testing.T) { testFile := filepath.Join(t.TempDir(), "testAnnotationFile") - os.WriteFile(testFile, []byte(testContent), fs.ModePerm) + err := os.WriteFile(testFile, []byte(testContent), fs.ModePerm) + if err != nil { + t.Fatalf("Error writing %s: %v", testFile, err) + } opts := Packer{AnnotationFilePath: testFile} anno, err := opts.LoadManifestAnnotations() @@ -120,11 +123,12 @@ func TestPacker_LoadManifestAnnotations_annotationFlag(t *testing.T) { t.Fatalf("unexpected error: failed when looking for '$manifest' in annotations") } if !reflect.DeepEqual(annotations, - map[string]map[string]string{"$manifest": { - "Key0": "", - "Key1": "Val", - "Key2": "${env:USERNAME}", - }, + map[string]map[string]string{ + "$manifest": { + "Key0": "", + "Key1": "Val", + "Key2": "${env:USERNAME}", + }, }) { t.Fatalf("unexpected error: %v", errors.New("content not match")) } diff --git a/cmd/oras/internal/option/remote.go b/cmd/oras/internal/option/remote.go index c4261a880..efd4e054a 100644 --- a/cmd/oras/internal/option/remote.go +++ b/cmd/oras/internal/option/remote.go @@ -273,7 +273,9 @@ func (opts *Remote) NewRepository(reference string, common Common) (repo *remote return nil, err } if opts.distributionSpec.referrersAPI != nil { - repo.SetReferrersCapability(*opts.distributionSpec.referrersAPI) + if err := repo.SetReferrersCapability(*opts.distributionSpec.referrersAPI); err != nil { + return nil, err + } } return } diff --git a/cmd/oras/internal/option/remote_test.go b/cmd/oras/internal/option/remote_test.go index efca51cc2..abd1c3029 100644 --- a/cmd/oras/internal/option/remote_test.go +++ b/cmd/oras/internal/option/remote_test.go @@ -51,7 +51,9 @@ func TestMain(m *testing.M) { case p == "/v2/" && m == "GET": w.WriteHeader(http.StatusOK) case p == fmt.Sprintf("/v2/%s/tags/list", testRepo) && m == "GET": - json.NewEncoder(w).Encode(testTagList) + if err := json.NewEncoder(w).Encode(testTagList); err != nil { + http.Error(w, "error encoding", http.StatusBadRequest) + } } })) defer ts.Close() @@ -233,7 +235,10 @@ func TestRemote_NewRepository_Retry(t *testing.T) { http.Error(w, "error", http.StatusTooManyRequests) return } - json.NewEncoder(w).Encode(testTagList) + err := json.NewEncoder(w).Encode(testTagList) + if err != nil { + http.Error(w, "error encoding", http.StatusBadRequest) + } })) defer ts.Close() opts := struct { diff --git a/cmd/oras/root/attach.go b/cmd/oras/root/attach.go index 99ed8ba14..d0a84dd13 100644 --- a/cmd/oras/root/attach.go +++ b/cmd/oras/root/attach.go @@ -94,7 +94,7 @@ Example - Attach file to the manifest tagged 'v1' in an OCI image layout folder cmd.Flags().StringVarP(&opts.artifactType, "artifact-type", "", "", "artifact type") cmd.Flags().IntVarP(&opts.concurrency, "concurrency", "", 5, "concurrency level") - cmd.MarkFlagRequired("artifact-type") + _ = cmd.MarkFlagRequired("artifact-type") opts.EnableDistributionSpecFlag() option.ApplyFlags(&opts, cmd.Flags()) return cmd diff --git a/cmd/oras/root/discover.go b/cmd/oras/root/discover.go index d853e77d2..b7dd372ae 100644 --- a/cmd/oras/root/discover.go +++ b/cmd/oras/root/discover.go @@ -133,7 +133,7 @@ func runDiscover(ctx context.Context, opts discoverOptions) error { fmt.Println("Digest:", desc.Digest) if len(refs) > 0 { fmt.Println() - printDiscoveredReferrersTable(refs, opts.Verbose) + _ = printDiscoveredReferrersTable(refs, opts.Verbose) } return nil } @@ -171,7 +171,7 @@ func fetchAllReferrers(ctx context.Context, repo oras.ReadOnlyGraphTarget, desc return nil } -func printDiscoveredReferrersTable(refs []ocispec.Descriptor, verbose bool) { +func printDiscoveredReferrersTable(refs []ocispec.Descriptor, verbose bool) error { typeNameTitle := "Artifact Type" typeNameLength := len(typeNameTitle) for _, ref := range refs { @@ -188,9 +188,12 @@ func printDiscoveredReferrersTable(refs []ocispec.Descriptor, verbose bool) { for _, ref := range refs { print(ref.ArtifactType, ref.Digest) if verbose { - printJSON(ref) + if err := printJSON(ref); err != nil { + return fmt.Errorf("Error printing JSON: %w", err) + } } } + return nil } // printDiscoveredReferrersJSON prints referrer list in JSON equivalent to the diff --git a/internal/cache/target_test.go b/internal/cache/target_test.go index cbc7cd0b0..508116774 100644 --- a/internal/cache/target_test.go +++ b/internal/cache/target_test.go @@ -177,7 +177,9 @@ func TestProxy_fetchReference(t *testing.T) { w.WriteHeader(http.StatusOK) // write data to the response if this is the first request if requestCount == 1 { - w.Write(blob) + if _, err := w.Write(blob); err != nil { + t.Errorf("Error writing blobs: %v", err) + } } atomic.AddInt64(&successCount, 1) return diff --git a/internal/credential/store_test.go b/internal/credential/store_test.go new file mode 100644 index 000000000..c7f94569b --- /dev/null +++ b/internal/credential/store_test.go @@ -0,0 +1,48 @@ +/* +Copyright The ORAS Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package credential + +import ( + "os" + "path" + "reflect" + "strings" + "testing" +) + +func TestNewStoreError(t *testing.T) { + tmpDir := t.TempDir() + filename := path.Join(tmpDir, "testfile.txt") + _, err := os.Create(filename) + if err != nil { + t.Errorf("error: cannot create file : %v", err) + } + err = os.Chmod(filename, 000) + if err != nil { + t.Errorf("error: cannot change file permissions: %v", err) + } + credStore, err := NewStore(filename) + if credStore != nil { + t.Errorf("Expected NewStore to return nil but actually returned %v ", credStore) + } + if err != nil { + ok := strings.Contains(err.Error(), "failed to open config file") + reflect.DeepEqual(ok, true) + + } else { + t.Errorf("Expected err to be not nil") + } +} diff --git a/internal/graph/graph_test.go b/internal/graph/graph_test.go index 50eb0e8e0..599307fa2 100644 --- a/internal/graph/graph_test.go +++ b/internal/graph/graph_test.go @@ -121,7 +121,7 @@ func TestReferrers(t *testing.T) { index ) anno := map[string]string{"test": "foo"} - appendBlob(ocispec.MediaTypeArtifactManifest, []byte("subject content")) + appendBlob(ocispec.MediaTypeArtifactManifest, []byte(`{"name":"subject content"}`)) imageType := "test.image" appendBlob(imageType, []byte("config content")) generateImage(nil, nil, descs[imgConfig], descs[blob]) @@ -139,7 +139,9 @@ func TestReferrers(t *testing.T) { referrers := []ocispec.Descriptor{descs[image], descs[image]} memory := memory.New() for i := range descs { - memory.Push(ctx, descs[i], bytes.NewReader(blobs[i])) + if err := memory.Push(ctx, descs[i], bytes.NewReader(blobs[i])); err != nil { + t.Errorf("Error pushing %v\n", err) + } } finder := &predecessorFinder{Store: memory} @@ -244,7 +246,7 @@ func TestSuccessors(t *testing.T) { artifact index ) - appendBlob(ocispec.MediaTypeArtifactManifest, []byte("subject content")) + appendBlob(ocispec.MediaTypeArtifactManifest, []byte(`{"name":"subject content"}`)) imageType := "test.image" appendBlob(imageType, []byte("config content")) generateImage(&descs[subject], ocispec.MediaTypeImageManifest, descs[config]) @@ -255,7 +257,9 @@ func TestSuccessors(t *testing.T) { memory := memory.New() ctx := context.Background() for i := range descs { - memory.Push(ctx, descs[i], bytes.NewReader(blobs[i])) + if err := memory.Push(ctx, descs[i], bytes.NewReader(blobs[i])); err != nil { + t.Errorf("Error pushing %v\n", err) + } } fetcher := &fetcher{Fetcher: memory} @@ -341,7 +345,7 @@ func TestFindReferrerPredecessors(t *testing.T) { image ) var anno map[string]string - appendBlob(ocispec.MediaTypeArtifactManifest, []byte("subject content")) + appendBlob(ocispec.MediaTypeArtifactManifest, []byte(`{"name":"subject content"}`)) imageType := "test.image" appendBlob(imageType, []byte("config content")) generateImage(&descs[subject], anno, descs[imgConfig]) @@ -353,7 +357,9 @@ func TestFindReferrerPredecessors(t *testing.T) { referrers := []ocispec.Descriptor{descs[image], descs[image]} memory := memory.New() for i := range descs { - memory.Push(ctx, descs[i], bytes.NewReader(blobs[i])) + if err := memory.Push(ctx, descs[i], bytes.NewReader(blobs[i])); err != nil { + t.Errorf("Error pushing %v\n", err) + } } finder := &predecessorFinder{Store: memory} type args struct { @@ -367,7 +373,6 @@ func TestFindReferrerPredecessors(t *testing.T) { want []ocispec.Descriptor wantErr bool }{ - {"should failed to get referrers", args{ctx, &errLister{}, ocispec.Descriptor{}}, nil, true}, {"should failed to get predecessor", args{ctx, &errFinder{}, ocispec.Descriptor{}}, nil, true}, {"should return referrers when target is a referrer lister", args{ctx, &refLister{referrers: referrers}, ocispec.Descriptor{}}, referrers, false}, diff --git a/internal/version/version.go b/internal/version/version.go index 684cd6aa5..5c7fb8b7c 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -17,7 +17,7 @@ package version var ( // Version is the current version of the oras. - Version = "1.1.0-rc.1" + Version = "1.1.0" // BuildMetadata is the extra build time data BuildMetadata = "unreleased" // GitCommit is the git sha1 diff --git a/test/e2e/suite/command/attach.go b/test/e2e/suite/command/attach.go index 499a613da..883ab2fed 100644 --- a/test/e2e/suite/command/attach.go +++ b/test/e2e/suite/command/attach.go @@ -293,7 +293,7 @@ var _ = Describe("OCI image layout users:", func() { root := PrepareTempFiles() subjectRef := LayoutRef(root, foobar.Tag) prepare(root) - ORAS("attach", "--artifact-type", "test.attach", "-v", Flags.Layout, subjectRef, fmt.Sprintf("%s:%s", foobar.AttachFileName, foobar.AttachFileMedia)). + ORAS("attach", "--artifact-type", "test.attach", "-v", Flags.Layout, subjectRef, fmt.Sprintf("%s:%s", foobar.AttachFileName, foobar.AttachFileMedia), "--skip-delete-referrers"). MatchErrKeyWords("referrers deletion can only be enforced upon registry\n"). WithWorkDir(root). Exec() diff --git a/test/e2e/suite/command/cp.go b/test/e2e/suite/command/cp.go index 5865ba0cb..3064c9977 100644 --- a/test/e2e/suite/command/cp.go +++ b/test/e2e/suite/command/cp.go @@ -364,7 +364,7 @@ var _ = Describe("OCI layout users:", func() { }) It("should copy and output verbosed warning for Feferrers deletion by default", func() { - ORAS("cp", RegistryRef(Host, ArtifactRepo, foobar.Tag), GinkgoT().TempDir(), Flags.ToLayout, "-v"). + ORAS("cp", RegistryRef(Host, ArtifactRepo, foobar.Tag), GinkgoT().TempDir(), Flags.ToLayout, "-v", "--skip-delete-referrers"). MatchErrKeyWords("referrers deletion can only be enforced upon registry\n"). Exec() }) diff --git a/test/e2e/suite/command/manifest.go b/test/e2e/suite/command/manifest.go index 36d30bf0c..51235314e 100644 --- a/test/e2e/suite/command/manifest.go +++ b/test/e2e/suite/command/manifest.go @@ -549,7 +549,7 @@ var _ = Describe("OCI image layout users:", func() { manifestPath := WriteTempFile("manifest.json", manifest) root := filepath.Dir(manifestPath) prepare(root) - ORAS("manifest", "push", root, Flags.Layout, manifestPath, "--skip-delete-referrers=false"). + ORAS("manifest", "push", root, Flags.Layout, manifestPath, "--skip-delete-referrers"). WithWorkDir(root). MatchErrKeyWords("referrers deletion can only be enforced upon registry\n").Exec() })