diff --git a/.all-contributorsrc b/.all-contributorsrc index f993e31fe..efef0367b 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -601,8 +601,45 @@ "contributions": [ "code" ] + }, + { + "login": "Kaiyang-Chen", + "name": "Kaiyang Chen", + "avatar_url": "https://avatars.githubusercontent.com/u/48289729?v=4", + "profile": "https://github.com/Kaiyang-Chen", + "contributions": [ + "code" + ] + }, + { + "login": "lxb1226", + "name": "heyjude", + "avatar_url": "https://avatars.githubusercontent.com/u/33415192?v=4", + "profile": "https://lxb1226.github.io/", + "contributions": [ + "code" + ] + }, + { + "login": "Electronic-Waste", + "name": "Shao Wang", + "avatar_url": "https://avatars.githubusercontent.com/u/77665902?v=4", + "profile": "http://blog.electronicwaste.cn", + "contributions": [ + "code" + ] + }, + { + "login": "arugal", + "name": "zhang-wei", + "avatar_url": "https://avatars.githubusercontent.com/u/26432832?v=4", + "profile": "http://blogs.zhangwei.asia", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, - "skipCi": true + "skipCi": true, + "commitType": "docs" } diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 4f9eccc40..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: 'bug: ' -labels: "type/bug \U0001F41B" - ---- - -## Description - -## Reproduction - -## Additional Info - -<!-- It will be very helpful if you can provide the version info by running the command `envd version`. --> - ---- -<!-- Issue Author: Don't delete this message to encourage other users to support your issue! --> -**Message from the maintainers**: - -Impacted by this bug? Give it a ๐Ÿ‘. We prioritise the issues with the most ๐Ÿ‘. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 000000000..efb9f47c6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,56 @@ +name: Bug Report +description: Bug report for envd +labels: ["type/bug \U0001F41B"] +title: "bug: <title>" +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + - type: checkboxes + attributes: + label: Are you use the envd server? + description: Please check if you are using the envd server. + options: + - label: Yes, I am using the envd server. + - label: No, I am not using the envd server. + - type: textarea + attributes: + label: Describe the bug + description: A clear and concise description of what the bug is. + validations: + required: true + - type: textarea + attributes: + label: To Reproduce + description: Steps to reproduce the behavior. + validations: + required: true + - type: textarea + attributes: + label: Expected behavior + description: A clear and concise description of what you expected to happen. + validations: + required: false + - type: textarea + attributes: + label: The `docker info` output + description: The output of `docker info` command. + validations: + required: true + - type: textarea + attributes: + label: The `envd version` output + description: The output of `envd version` command. + validations: + required: true + - type: textarea + attributes: + label: Additional context + description: Add any other context about the problem here. + validations: + required: false + - type: markdown + attributes: + value: | + Impacted by this bug? Give it a ๐Ÿ‘. We prioritise the issues with the most ๐Ÿ‘. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md deleted file mode 100644 index 3c7b935ea..000000000 --- a/.github/ISSUE_TEMPLATE/feature-request.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: 'feat: <title>' -labels: "type/feature \U0001F4A1" - ---- - -## Description - ---- -<!-- Issue Author: Don't delete this message to encourage other users to support your issue! --> -**Message from the maintainers**: - -Love this enhancement proposal? Give it a ๐Ÿ‘. We prioritise the proposals with the most ๐Ÿ‘. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml new file mode 100644 index 000000000..d12a7ccc2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -0,0 +1,31 @@ +name: Feature Request +description: Feature request for envd +labels: ["type/feature \U0001F4A1"] +title: "feat: <title>" +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this feature request! + - type: textarea + attributes: + label: Describe the feature + description: A clear and concise description of what the feature is. + validations: + required: true + - type: textarea + attributes: + label: Why do you need this feature? + description: A clear and concise description of why you need this feature. + validations: + required: false + - type: textarea + attributes: + label: Additional context + description: Add any other context about the problem here. + validations: + required: false + - type: markdown + attributes: + value: | + Love this enhancement proposal? Give it a ๐Ÿ‘. We prioritise the proposals with the most ๐Ÿ‘. \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ecb073748..ab2ea2f24 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,17 +3,17 @@ updates: - package-ecosystem: "gomod" directory: "/" schedule: - interval: "weekly" - day: "monday" - time: "10:00" - timezone: "Asia/Shanghai" - open-pull-requests-limit: 5 + interval: "monthly" + groups: + all-dependencies: + patterns: + - "*" - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "weekly" - day: "monday" - time: "10:00" - timezone: "Asia/Shanghai" - open-pull-requests-limit: 5 + interval: "monthly" + groups: + all-actions: + patterns: + - "*" diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 0e3ead6f7..9a928e96a 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -5,50 +5,57 @@ on: branches: - main paths: - - '.github/workflows/**' + - '.github/workflows/CI.yml' - '**.go' - 'Makefile' - 'go.**' + - 'pkg/**/*.sh' + - 'pkg/**/*.envd' pull_request: paths: - - '.github/workflows/**' + - '.github/workflows/CI.yml' - '**.go' - 'Makefile' - 'go.**' + - 'pkg/**/*.sh' + - 'pkg/**/*.envd' + merge_group: + workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: + typos-check: + name: Spell Check with Typos + runs-on: ubuntu-24.04 + steps: + - name: Checkout Actions Repository + uses: actions/checkout@v4 + - name: Check spelling with custom config file + uses: crate-ci/typos@master + with: + config: ./typos.toml lint: name: lint - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - - uses: actions/setup-go@v3 - with: - go-version: 1.18 - - uses: actions/checkout@v3 - - name: Cache Go modules - uses: actions/cache@preview + - uses: actions/setup-go@v5 with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-build-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.OS }}-build-${{ env.cache-name }}- - ${{ runner.OS }}-build- - ${{ runner.OS }}- + go-version: 'stable' + - uses: actions/checkout@v4 - name: Add license run: | make addlicense && git add pkg cmd && git diff --cached --exit-code || (echo 'Please run "make addlicense" to verify govet' && exit 1); - name: golangci-lint - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@v8 with: args: --timeout=5m version: latest # Ref https://github.com/golangci/golangci-lint-action/issues/244 - skip-pkg-cache: true + skip-cache: true test: name: test env: @@ -56,24 +63,15 @@ jobs: ENVD_ANALYTICS: false strategy: matrix: - os: [ubuntu-latest] + os: [ubuntu-24.04] runs-on: ${{ matrix.os }} steps: - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: - go-version: 1.18 - - name: Cache Go modules - uses: actions/cache@preview - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-build-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.OS }}-build-${{ env.cache-name }}- - ${{ runner.OS }}-build- - ${{ runner.OS }}- + go-version: 'stable' - name: Verify mockgen run: | make generate && git add pkg && @@ -81,7 +79,7 @@ jobs: - name: Test run: make test - name: Upload coverage report - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: coverage-out path: coverage.out @@ -92,24 +90,15 @@ jobs: ENVD_ANALYTICS: false strategy: matrix: - os: [ubuntu-latest] + os: [ubuntu-22.04] runs-on: ${{ matrix.os }} steps: - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Go - uses: actions/setup-go@v3 - with: - go-version: 1.18 - - name: Cache Go modules - uses: actions/cache@preview + uses: actions/setup-go@v5 with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-build-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.OS }}-build-${{ env.cache-name }}- - ${{ runner.OS }}-build- - ${{ runner.OS }}- + go-version: 'stable' - uses: actions-ecosystem/action-get-latest-tag@v1 id: get-latest-tag - name: e2e test @@ -117,46 +106,10 @@ jobs: env: GIT_LATEST_TAG: ${{ steps.get-latest-tag.outputs.tag }} - name: Upload coverage report - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: e2e-cli-coverage-out path: e2e-cli-coverage.out - e2e-cli-v1: - name: e2e-cli-v1 - env: - # Disable telemetry. - ENVD_ANALYTICS: false - strategy: - matrix: - os: [ubuntu-latest] - runs-on: ${{ matrix.os }} - steps: - - name: Check out code - uses: actions/checkout@v3 - - name: Setup Go - uses: actions/setup-go@v3 - with: - go-version: 1.18 - - name: Cache Go modules - uses: actions/cache@preview - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-build-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.OS }}-build-${{ env.cache-name }}- - ${{ runner.OS }}-build- - ${{ runner.OS }}- - - uses: actions-ecosystem/action-get-latest-tag@v1 - id: get-latest-tag - - name: e2e test - run: make e2e-cli-test-v1 - env: - GIT_LATEST_TAG: ${{ steps.get-latest-tag.outputs.tag }} - - name: Upload coverage report - uses: actions/upload-artifact@v3 - with: - name: e2e-cli-v1-coverage-out - path: e2e-cli-v1-coverage.out e2e-lang: name: e2e-lang env: @@ -164,24 +117,15 @@ jobs: ENVD_ANALYTICS: false strategy: matrix: - os: [ubuntu-latest] + os: [ubuntu-22.04] runs-on: ${{ matrix.os }} steps: - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Go - uses: actions/setup-go@v3 - with: - go-version: 1.18 - - name: Cache Go modules - uses: actions/cache@preview + uses: actions/setup-go@v5 with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-build-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.OS }}-build-${{ env.cache-name }}- - ${{ runner.OS }}-build- - ${{ runner.OS }}- + go-version: 'stable' - uses: actions-ecosystem/action-get-latest-tag@v1 id: get-latest-tag - name: e2e test @@ -189,46 +133,10 @@ jobs: env: GIT_LATEST_TAG: ${{ steps.get-latest-tag.outputs.tag }} - name: Upload coverage report - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: e2e-lang-coverage-out path: e2e-lang-coverage.out - e2e-lang-v1: - name: e2e-lang-v1 - env: - # Disable telemetry. - ENVD_ANALYTICS: false - strategy: - matrix: - os: [ubuntu-latest] - runs-on: ${{ matrix.os }} - steps: - - name: Check out code - uses: actions/checkout@v3 - - name: Setup Go - uses: actions/setup-go@v3 - with: - go-version: 1.18 - - name: Cache Go modules - uses: actions/cache@preview - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-build-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.OS }}-build-${{ env.cache-name }}- - ${{ runner.OS }}-build- - ${{ runner.OS }}- - - uses: actions-ecosystem/action-get-latest-tag@v1 - id: get-latest-tag - - name: e2e test - run: make e2e-lang-test-v1 - env: - GIT_LATEST_TAG: ${{ steps.get-latest-tag.outputs.tag }} - - name: Upload coverage report - uses: actions/upload-artifact@v3 - with: - name: e2e-lang-v1-coverage-out - path: e2e-lang-v1-coverage.out build: name: build env: @@ -236,24 +144,15 @@ jobs: ENVD_ANALYTICS: false strategy: matrix: - os: [ubuntu-latest, macos-latest] + os: [ubuntu-24.04, macos-latest] runs-on: ${{ matrix.os }} steps: - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: - go-version: 1.18 - - name: Cache Go modules - uses: actions/cache@preview - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-build-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.OS }}-build-${{ env.cache-name }}- - ${{ runner.OS }}-build- - ${{ runner.OS }}- + go-version: 'stable' - name: Build run: make @@ -263,45 +162,33 @@ jobs: - test - e2e-cli - e2e-lang - - e2e-cli-v1 - - e2e-lang-v1 - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: - go-version: 1.18 + go-version: 'stable' - name: Install bins run: | go install github.com/mattn/goveralls@latest - name: Get coverage report - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: coverage-out path: merge - name: Get cli e2e coverage report - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: e2e-cli-coverage-out path: merge - name: Get language e2e coverage report - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: e2e-lang-coverage-out path: merge - - name: Get cli v1 e2e coverage report - uses: actions/download-artifact@v3 - with: - name: e2e-cli-v1-coverage-out - path: merge - - name: Get language v1 e2e coverage report - uses: actions/download-artifact@v3 - with: - name: e2e-lang-v1-coverage-out - path: merge - name: Merge all coverage reports uses: cutecutecat/go-cover-merge@v1 with: diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml deleted file mode 100644 index 9d18fde17..000000000 --- a/.github/workflows/auto-merge.yml +++ /dev/null @@ -1,42 +0,0 @@ -# https://docs.github.com/en/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions -name: auto-merge for dependabot and changelog PRs -on: pull_request - -permissions: - pull-requests: write - contents: write - -jobs: - dependabot: - runs-on: ubuntu-latest - if: ${{ github.actor == 'dependabot[bot]' }} - steps: - - name: Dependabot metadata - id: metadata - uses: dependabot/fetch-metadata@v1.3.5 - with: - github-token: "${{ secrets.GITHUB_TOKEN }}" - - name: Approve PR - run: gh pr review --approve "$PR_URL" - env: - PR_URL: ${{github.event.pull_request.html_url}} - GH_TOKEN: ${{secrets.GITHUB_TOKEN}} - - name: Enable auto-merge for Dependabot PRs - run: gh pr merge --auto --squash "$PR_URL" - env: - PR_URL: ${{github.event.pull_request.html_url}} - GH_TOKEN: ${{secrets.GITHUB_TOKEN}} - changelog: - runs-on: ubuntu-latest - if: ${{ github.actor == 'github-actions[bot]' }} - steps: - - name: Approve PR - run: gh pr review --approve "$PR_URL" - env: - PR_URL: ${{github.event.pull_request.html_url}} - GH_TOKEN: ${{secrets.GITHUB_TOKEN}} - - name: Enable auto-merge for Changelog PRs - run: gh pr merge --auto --squash "$PR_URL" - env: - PR_URL: ${{github.event.pull_request.html_url}} - GH_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/changelog.yaml b/.github/workflows/changelog.yaml index 6a3970948..a60e5e267 100644 --- a/.github/workflows/changelog.yaml +++ b/.github/workflows/changelog.yaml @@ -10,14 +10,14 @@ jobs: runs-on: ubuntu-latest name: Generate changelog steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: main fetch-depth: 0 - run: git fetch --prune --prune-tags - run: git tag -l 'v*' - run: ./hack/changelog.sh > CHANGELOG.md - - uses: peter-evans/create-pull-request@v4 + - uses: peter-evans/create-pull-request@v7 with: title: 'docs: updated CHANGELOG.md' commit-message: 'docs: updated CHANGELOG.md' diff --git a/.github/workflows/envd-lint.yml b/.github/workflows/envd-lint.yml index b06ca92bf..880768bf4 100644 --- a/.github/workflows/envd-lint.yml +++ b/.github/workflows/envd-lint.yml @@ -17,6 +17,8 @@ on: - '**.py' - '**.ipynb' - 'Makefile' + merge_group: + workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -27,9 +29,9 @@ jobs: name: envd-lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: - python-version: 3.8 + python-version: 3.12 - name: Lint run: make envd-lint diff --git a/.github/workflows/link-check.yml b/.github/workflows/link-check.yml index 1421dc0bc..1b6ac8238 100644 --- a/.github/workflows/link-check.yml +++ b/.github/workflows/link-check.yml @@ -1,26 +1,27 @@ -name: documentation-check +name: Link check on: push: branches: - main paths: - - ".github/workflows/**" + - ".github/workflows/link-check.yml" - "**.md" pull_request: paths: - - ".github/workflows/**" + - ".github/workflows/link-check.yml" - "**.md" + workflow_dispatch: jobs: - markdown-link-check: + linkChecker: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: gaurav-nelson/github-action-markdown-link-check@v1 + - uses: actions/checkout@v4 + + - name: Link Checker + id: lychee + uses: lycheeverse/lychee-action@v2 with: - file-path: "README.md" - folder-path: "docs" - check-modified-files-only: yes - base-branch: main - config-file: .markdown-lint.json + fail: true + args: --verbose --no-progress --format detailed . diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 982515b86..58de2eaf5 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -16,30 +16,37 @@ jobs: if: github.repository == 'tensorchord/envd' runs-on: ubuntu-latest steps: - - uses: actions/setup-go@v3 + - uses: actions/setup-go@v5 with: - go-version: 1.18 - - uses: actions/checkout@v3 - - name: Cache Go modules - uses: actions/cache@preview - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-build-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.OS }}-build-${{ env.cache-name }}- - ${{ runner.OS }}-build- - ${{ runner.OS }}- + go-version: 'stable' + - uses: actions/checkout@v4 - name: Add license run: | make addlicense && git add pkg cmd && git diff --cached --exit-code || (echo 'Please run "make addlicense" to verify govet' && exit 1); - name: golangci-lint - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@v8 with: args: --timeout=5m version: latest # Ref https://github.com/golangci/golangci-lint-action/issues/244 - skip-pkg-cache: true + skip-cache: true + build: + name: build + if: github.repository == 'tensorchord/envd' + strategy: + matrix: + os: [ ubuntu-latest, macos-latest ] + runs-on: ${{ matrix.os }} + steps: + - name: Check out code + uses: actions/checkout@v4 + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: 'stable' + - name: Build + run: make test: name: test if: github.repository == 'tensorchord/envd' @@ -52,315 +59,74 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: - go-version: 1.18 - - name: Cache Go modules - uses: actions/cache@preview - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-build-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.OS }}-build-${{ env.cache-name }}- - ${{ runner.OS }}-build- - ${{ runner.OS }}- + go-version: 'stable' - name: Verify mockgen run: | make generate && git add pkg && git diff --cached --exit-code || (echo 'Please run "make generate" to verify generate' && exit 1); - name: Test run: make test - - name: Upload coverage report - uses: actions/upload-artifact@v3 - with: - name: coverage-out - path: coverage.out e2e-cli: name: e2e-cli if: github.repository == 'tensorchord/envd' env: # Disable telemetry. ENVD_ANALYTICS: false - strategy: - matrix: - os: [ ubuntu-latest ] - runs-on: ${{ matrix.os }} + runs-on: ubuntu-22.04 steps: - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Go - uses: actions/setup-go@v3 - with: - go-version: 1.18 - - name: Cache Go modules - uses: actions/cache@preview + uses: actions/setup-go@v5 with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-build-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.OS }}-build-${{ env.cache-name }}- - ${{ runner.OS }}-build- - ${{ runner.OS }}- + go-version: 'stable' - uses: actions-ecosystem/action-get-latest-tag@v1 id: get-latest-tag - name: e2e test run: make e2e-cli-test env: GIT_LATEST_TAG: ${{ steps.get-latest-tag.outputs.tag }} - - name: Upload coverage report - uses: actions/upload-artifact@v3 - with: - name: e2e-cli-coverage-out - path: e2e-cli-coverage.out e2e-lang: name: e2e-lang if: github.repository == 'tensorchord/envd' env: # Disable telemetry. ENVD_ANALYTICS: false - strategy: - matrix: - os: [ ubuntu-latest ] - runs-on: ${{ matrix.os }} + runs-on: ubuntu-22.04 steps: - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Go - uses: actions/setup-go@v3 - with: - go-version: 1.18 - - name: Cache Go modules - uses: actions/cache@preview + uses: actions/setup-go@v5 with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-build-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.OS }}-build-${{ env.cache-name }}- - ${{ runner.OS }}-build- - ${{ runner.OS }}- + go-version: 'stable' - uses: actions-ecosystem/action-get-latest-tag@v1 id: get-latest-tag - name: e2e test run: make e2e-lang-test env: GIT_LATEST_TAG: ${{ steps.get-latest-tag.outputs.tag }} - - name: Upload coverage report - uses: actions/upload-artifact@v3 - with: - name: e2e-lang-coverage-out - path: e2e-lang-coverage.out - # notifies that all test jobs are finished. - report: - if: github.repository == 'tensorchord/envd' - needs: - - test - - e2e-cli - - e2e-lang - runs-on: ubuntu-latest - steps: - - name: Check out code - uses: actions/checkout@v3 - - name: Setup Go - uses: actions/setup-go@v3 - with: - go-version: 1.18 - - name: Install bins - run: | - go install github.com/mattn/goveralls@latest - go install github.com/wadey/gocovmerge@latest - - name: Get coverage report - uses: actions/download-artifact@v3 - with: - name: coverage-out - path: coverage.out - - name: Get cli e2e coverage report - uses: actions/download-artifact@v3 - with: - name: e2e-cli-coverage-out - path: e2e-cli-coverage.out - - name: Get language e2e coverage report - uses: actions/download-artifact@v3 - with: - name: e2e-lang-coverage-out - path: e2e-lang-coverage.out - # - name: Send coverage - # env: - # COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # run: | - # gocovmerge e2e-coverage.out coverage.out > final.out - # goveralls -coverprofile=final.out -service=github - build: - name: build - if: github.repository == 'tensorchord/envd' - strategy: - matrix: - os: [ ubuntu-latest, macos-latest ] - runs-on: ${{ matrix.os }} - steps: - - name: Check out code - uses: actions/checkout@v3 - - name: Setup Go - uses: actions/setup-go@v3 - with: - go-version: 1.18 - - name: Cache Go modules - uses: actions/cache@preview - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-build-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.OS }}-build-${{ env.cache-name }}- - ${{ runner.OS }}-build- - ${{ runner.OS }}- - - name: Build - run: make e2e-doc: name: e2e-doc if: github.repository == 'tensorchord/envd' env: # Disable telemetry. ENVD_ANALYTICS: false - strategy: - matrix: - os: [ubuntu-latest] - runs-on: ${{ matrix.os }} + runs-on: ubuntu-22.04 steps: - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Go - uses: actions/setup-go@v3 - with: - go-version: 1.18 - - name: Cache Go modules - uses: actions/cache@preview + uses: actions/setup-go@v5 with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-build-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.OS }}-build-${{ env.cache-name }}- - ${{ runner.OS }}-build- - ${{ runner.OS }}- + go-version: 'stable' - uses: actions-ecosystem/action-get-latest-tag@v1 id: get-latest-tag - name: e2e doc test run: make e2e-doc-test env: GIT_LATEST_TAG: ${{ steps.get-latest-tag.outputs.tag }} - - name: Upload coverage report - uses: actions/upload-artifact@v3 - with: - name: e2e-doc-coverage-out - path: e2e-doc-coverage.out - e2e-cli-v1: - name: e2e-cli-v1 - if: github.repository == 'tensorchord/envd' - env: - # Disable telemetry. - ENVD_ANALYTICS: false - strategy: - matrix: - os: [ubuntu-latest] - runs-on: ${{ matrix.os }} - steps: - - name: Check out code - uses: actions/checkout@v3 - - name: Setup Go - uses: actions/setup-go@v3 - with: - go-version: 1.18 - - name: Cache Go modules - uses: actions/cache@preview - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-build-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.OS }}-build-${{ env.cache-name }}- - ${{ runner.OS }}-build- - ${{ runner.OS }}- - - uses: actions-ecosystem/action-get-latest-tag@v1 - id: get-latest-tag - - name: e2e test - run: make e2e-cli-test-v1 - env: - GIT_LATEST_TAG: ${{ steps.get-latest-tag.outputs.tag }} - - name: Upload coverage report - uses: actions/upload-artifact@v3 - with: - name: e2e-cli-v1-coverage-out - path: e2e-cli-v1-coverage.out - e2e-lang-v1: - name: e2e-lang-v1 - if: github.repository == 'tensorchord/envd' - env: - # Disable telemetry. - ENVD_ANALYTICS: false - strategy: - matrix: - os: [ubuntu-latest] - runs-on: ${{ matrix.os }} - steps: - - name: Check out code - uses: actions/checkout@v3 - - name: Setup Go - uses: actions/setup-go@v3 - with: - go-version: 1.18 - - name: Cache Go modules - uses: actions/cache@preview - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-build-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.OS }}-build-${{ env.cache-name }}- - ${{ runner.OS }}-build- - ${{ runner.OS }}- - - uses: actions-ecosystem/action-get-latest-tag@v1 - id: get-latest-tag - - name: e2e test - run: make e2e-lang-test-v1 - env: - GIT_LATEST_TAG: ${{ steps.get-latest-tag.outputs.tag }} - - name: Upload coverage report - uses: actions/upload-artifact@v3 - with: - name: e2e-lang-v1-coverage-out - path: e2e-lang-v1-coverage.out - e2e-doc-v1: - name: e2e-doc-v1 - if: github.repository == 'tensorchord/envd' - env: - # Disable telemetry. - ENVD_ANALYTICS: false - strategy: - matrix: - os: [ubuntu-latest] - runs-on: ${{ matrix.os }} - steps: - - name: Check out code - uses: actions/checkout@v3 - - name: Setup Go - uses: actions/setup-go@v3 - with: - go-version: 1.18 - - name: Cache Go modules - uses: actions/cache@preview - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-build-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.OS }}-build-${{ env.cache-name }}- - ${{ runner.OS }}-build- - ${{ runner.OS }}- - - uses: actions-ecosystem/action-get-latest-tag@v1 - id: get-latest-tag - - name: e2e doc test - run: make e2e-doc-test-v1 - env: - GIT_LATEST_TAG: ${{ steps.get-latest-tag.outputs.tag }} - - name: Upload coverage report - uses: actions/upload-artifact@v3 - with: - name: e2e-doc-v1-coverage-out - path: e2e-doc-v1-coverage.out diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 58fad9bcc..c24f19945 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,43 +1,37 @@ name: release on: - push: - tags: - - 'v*' - pull_request: - paths: - - '.github/workflows/release.yml' - - '.goreleaser/' - - '.goreleaser.yaml' + release: + types: [published] + workflow_dispatch: jobs: goreleaser: - if: github.repository == 'tensorchord/envd' runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: - go-version: 1.18 - - name: Docker Login - uses: docker/login-action@v2 + go-version: 'stable' + - name: Login to Docker + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERIO_USERNAME }} password: ${{ secrets.DOCKERIO_TOKEN }} - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v3 + uses: goreleaser/goreleaser-action@v6 with: distribution: goreleaser - version: latest - args: release --rm-dist + version: "~> v2" + args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: upload gobin - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: gobin_${{ github.event.release.tag_name }} retention-days: 1 @@ -45,80 +39,98 @@ jobs: dist/envd_linux_amd64_v1/envd dist/envd_darwin_all/envd if-no-files-found: error - pypi_publish: + python_build: needs: goreleaser - # only trigger on main repo when tag starts with v - if: github.repository == 'tensorchord/envd' && startsWith(github.ref, 'refs/tags/v') runs-on: ${{ matrix.os }} - timeout-minutes: 20 strategy: matrix: - os: [macos-11, ubuntu-20.04] + os: [macos-13, ubuntu-22.04] steps: - - uses: actions/checkout@v3 - - name: Get gobin - uses: actions/download-artifact@v3 - with: - name: gobin_${{ github.event.release.tag_name }} - path: dist/ - - name: Configure linux build environment - if: runner.os == 'Linux' - run: | - mkdir -p bin - mv dist/envd_linux_amd64_v1/envd bin/envd - chmod +x bin/envd - - name: Configure macOS build environment - if: runner.os == 'macOS' - run: | - mkdir -p bin - mv dist/envd_darwin_all/envd bin/envd - chmod +x bin/envd - - name: Build wheels - uses: pypa/cibuildwheel@v2.12.0 - env: - CIBW_ARCHS_MACOS: arm64, x86_64 - CIBW_ARCHS_LINUX: auto64 - CIBW_SKIP: pp* - - name: Build source distribution - if: runner.os == 'Linux' # Only release source under linux to avoid conflict - run: | - python3 -m pip install wheel - python3 setup.py sdist - mv dist/*.tar.gz wheelhouse/ - - name: Upload to PyPI - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} - run: | - python -m pip install --upgrade pip - python -m pip install twine - python -m twine upload wheelhouse/* - image_publish: - name: Build & push images + - uses: actions/checkout@v4 + - name: Get gobin + uses: actions/download-artifact@v4 + with: + name: gobin_${{ github.event.release.tag_name }} + path: dist/ + - name: Configure linux build environment + if: runner.os == 'Linux' + run: | + mkdir -p bin + mv dist/envd_linux_amd64_v1/envd bin/envd + chmod +x bin/envd + - name: Configure macOS build environment + if: runner.os == 'macOS' + run: | + mkdir -p bin + mv dist/envd_darwin_all/envd bin/envd + chmod +x bin/envd + - name: setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + - name: Build wheels + uses: pypa/cibuildwheel@v2.23.3 + env: + CIBW_ARCHS_MACOS: arm64, x86_64 + CIBW_ARCHS_LINUX: auto64 + CIBW_SKIP: pp* + - name: Build source distribution + if: runner.os == 'Linux' # Only release source under linux to avoid conflict + run: | + python -m pip install wheel + python setup.py sdist + mv dist/*.tar.gz wheelhouse/ + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: pypi_${{ github.event.release.tag_name }}_${{ matrix.os }} + path: wheelhouse/ + retention-days: 1 + pypi_publish: + needs: python_build # only trigger on main repo when tag starts with v if: github.repository == 'tensorchord/envd' && startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-24.04 + permissions: + id-token: write # IMPORTANT: this permission is mandatory for trusted publishing + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + pattern: pypi_${{ github.event.release.tag_name }}_* + merge-multiple: true + path: dist/ + - name: Upload to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + skip-existing: true + verbose: true + envd_starship_publish: + name: Push starship image to Docker Hub runs-on: ubuntu-latest + # only trigger on main repo when tag starts with v + if: github.repository == 'tensorchord/envd' && startsWith(github.ref, 'refs/tags/v') needs: goreleaser steps: - - uses: actions/checkout@v3 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - name: Cache Docker layers - uses: actions/cache@v3 - id: cache - with: - path: /tmp/.buildx-cache - key: ${{ runner.os }}-buildx-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-buildx- + - uses: actions/checkout@v4 - name: Docker Login - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERIO_USERNAME }} password: ${{ secrets.DOCKERIO_TOKEN }} - - name: Docker Buildx - run: | - ./base-images/build.sh + - name: Docker Setup QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build and push + uses: docker/build-push-action@v6 + with: + push: true + file: base-images/envd-starship/envd-starship.Dockerfile + platforms: linux/amd64,linux/arm64 + tags: tensorchord/starship:v0.0.1 + cache-from: type=gha + cache-to: type=gha,mode=max envd_image_push: name: Build & push envd images # only trigger on main repo when tag starts with v @@ -126,11 +138,11 @@ jobs: runs-on: ubuntu-latest needs: goreleaser steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Cache Docker layers - uses: actions/cache@v3 + uses: actions/cache@v4 id: cache with: path: /tmp/.buildx-cache @@ -138,7 +150,7 @@ jobs: restore-keys: | ${{ runner.os }}-buildx- - name: Docker Login - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERIO_USERNAME }} password: ${{ secrets.DOCKERIO_TOKEN }} @@ -151,24 +163,20 @@ jobs: if: github.repository == 'tensorchord/envd' && startsWith(github.ref, 'refs/tags/v') runs-on: ubuntu-latest strategy: + fail-fast: false matrix: - build_func: ["build", "build_gpu_11_2", "build_gpu_11_3", "build_gpu_11_6"] include: - build_func: build tag_suffix: "" - - build_func: build_gpu_11_2 - tag_suffix: "-cuda-11.2.2-cudnn-8" - - build_func: build_gpu_11_3 - tag_suffix: "-cuda-11.3.1-cudnn-8" - - build_func: build_gpu_11_6 - tag_suffix: "-cuda-11.6.2-cudnn-8" - needs: image_publish + - build_func: build_gpu_11_8 + tag_suffix: "-cuda-11.8.0-cudnn-8" + needs: goreleaser steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Cache Docker layers - uses: actions/cache@v3 + uses: actions/cache@v4 id: cache with: path: /tmp/.buildx-cache @@ -176,7 +184,7 @@ jobs: restore-keys: | ${{ runner.os }}-buildx- - name: Get gobin - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: gobin_${{ github.event.release.tag_name }} path: dist/ @@ -186,7 +194,7 @@ jobs: mv dist/envd_linux_amd64_v1/envd /usr/local/bin/envd chmod +x /usr/local/bin/envd - name: Docker Login - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERIO_USERNAME }} password: ${{ secrets.DOCKERIO_TOKEN }} @@ -194,5 +202,5 @@ jobs: run: ./base-images/remote-cache/build-and-push-remote-cache.sh env: BUILD_FUNC: ${{ matrix.build_func }} - TAG_SUFFIX: ${{ matrix.TAG_SUFFIX }} + TAG_SUFFIX: ${{ matrix.tag_suffix }} ENVD_BUILD_OWNER: 1000 diff --git a/.gitignore b/.gitignore index 82056c4d9..7f9ac9c8b 100644 --- a/.gitignore +++ b/.gitignore @@ -67,5 +67,9 @@ share/python-wheels/ *.egg MANIFEST .GIT_TAG_INFO +.pixi/ test.tar + +# ruff +.ruff_cache/ diff --git a/.golangci.yml b/.golangci.yml index 59eb0cbb1..01ca2038d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,21 +1,53 @@ +version: "2" linters: enable: - - gofmt + - staticcheck - bodyclose - - errcheck - - goimports + - copyloopvar - errorlint - - exportloopref - - gosimple - - govet - - ineffassign - misspell - - rowserrcheck + - revive - sqlclosecheck - - staticcheck - - typecheck + - unconvert - unparam - - unused -linters-settings: - goimports: - local-prefixes: github.com/tensorchord/envd/ + settings: + revive: + rules: + - name: indent-error-flow + staticcheck: + checks: + - all + - -ST1000 + - -ST1003 + - -ST1016 + - -ST1020 + - -ST1021 + - -ST1022 + - -QF1001 + - -QF1007 + - -QF1008 + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + paths: + - third_party$ + - builtin$ + - examples$ +formatters: + enable: + - gofmt + - goimports + settings: + goimports: + local-prefixes: + - github.com/tensorchord/envd/ + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 13eadcd52..a460377a9 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -1,5 +1,9 @@ # This is an example .goreleaser.yml file with some sensible defaults. # Make sure to check the documentation at https://goreleaser.com +version: 2 +git: + # if there are more than one tag in the same commit. + tag_sort: -version:creatordate before: hooks: - go mod tidy @@ -41,26 +45,26 @@ archives: format: binary builds: - envd - replacements: - darwin: Darwin - linux: Linux - windows: Windows - 386: i386 - amd64: x86_64 + name_template: >- + {{ .Binary }}_{{ .Version }}_ + {{- title .Os }}_ + {{- if eq .Arch "amd64" }}x86_64 + {{- else if eq .Arch "386" }}i386 + {{- else }}{{ .Arch }}{{ end }} - id: envd-sshd format: binary builds: - envd-sshd - replacements: - darwin: Darwin - linux: Linux - windows: Windows - 386: i386 - amd64: x86_64 + name_template: >- + {{ .Binary }}_{{ .Version }}_ + {{- title .Os }}_ + {{- if eq .Arch "amd64" }}x86_64 + {{- else if eq .Arch "386" }}i386 + {{- else }}{{ .Arch }}{{ end }} checksum: name_template: 'checksums.txt' snapshot: - name_template: "{{ incpatch .Version }}-next" + version_template: "{{ incpatch .Version }}-next" changelog: use: github sort: asc @@ -123,23 +127,3 @@ docker_manifests: image_templates: - tensorchord/envd-from-scratch:v{{ .Version }}-amd64 - tensorchord/envd-from-scratch:v{{ .Version }}-arm64v8 -# See https://github.com/tensorchord/envd/issues/908 -# brews: -# - name: envd -# ids: -# - envd -# tap: -# owner: tensorchord -# name: homebrew-tap -# commit_author: -# name: TensorChord -# email: envd-maintainers@tensorchord.ai -# folder: Formula -# homepage: https://envd.tensorchord.ai/ -# description: Development environment for data science and AI/ML teams -# license: Apache-2.0 -# skip_upload: auto -# caveats: | -# Please run `envd bootstrap` first to bootstrap -# test: | -# system "#{bin}/envd version" diff --git a/.lycheeignore b/.lycheeignore new file mode 100644 index 000000000..b8d629203 --- /dev/null +++ b/.lycheeignore @@ -0,0 +1,7 @@ +# blog no longer exists +https://www.iam.rw/ +http://lizheming.top/ +https://bandism.net/ +https://blog.thrimbda.com/ +# github disappeared (rename?) +https://github.com/yczheng0 diff --git a/.markdown-lint.json b/.markdown-lint.json deleted file mode 100644 index 231809a08..000000000 --- a/.markdown-lint.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "ignorePatterns": [ - { - "pattern": "^http://localhost.*" - }, - ] -} \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 51f916370..9a27201df 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,3 +4,7 @@ repos: hooks: - id: golangci-lint args: [--config=.golangci.yml, --timeout=3m] + - repo: https://github.com/crate-ci/typos + rev: v1.13.10 + hooks: + - id: typos diff --git a/CHANGELOG.md b/CHANGELOG.md index 576d0c127..593ed4d86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,493 @@ # Changelog +## v1.2.1 (2025-06-17) + + * [599a980](https://github.com/tensorchord/envd/commit/599a9802480418a72b842b34c3094b8dc62a1e05) fix: sshd handle the error (#2026) + * [b964594](https://github.com/tensorchord/envd/commit/b96459498b2131d80446f46822a2a67eeed938ed) refact: bump tablewriter to v1, fix the breaking change (#2024) + * [a1a315e](https://github.com/tensorchord/envd/commit/a1a315e8e901fb5c04d52d4c8c0b59fe4da12be5) feat: add pixi template (#2014) + * [5f1f397](https://github.com/tensorchord/envd/commit/5f1f397ed6fbe32bd6b40c31300ebe12b7643155) feat: support pixi as an alternative to conda (#2012) + +### Contributors + + * Keming + +## v1.2.0 (2025-04-02) + + * [8514a4a](https://github.com/tensorchord/envd/commit/8514a4aee1bc96c9677995cedc0cc93e20063670) fix: locale for both dev=True/False (#2010) + * [2506ede](https://github.com/tensorchord/envd/commit/2506ede9f274403e66360029f98f6bd12faac243) feat: bump fish to the official release version (#2000) + * [a78a245](https://github.com/tensorchord/envd/commit/a78a2454ca633ad403b12920ef71a534f8b819d5) feat: emb the whole template/*.envd, avoid random map key order (#1995) + +### Contributors + + * Keming + +## v1.1.0 (2025-03-10) + + * [207c30e](https://github.com/tensorchord/envd/commit/207c30ee9556bb6915996166507fbc81ec26309a) feat: add pre-defined envd template (#1991) + * [1089f98](https://github.com/tensorchord/envd/commit/1089f98474ca85f453c90366acecf9f6a505aad0) feat: support uv (#1990) + +### Contributors + + * Keming + +## v1.0.1 (2025-02-17) + + +### Contributors + + +## v1.0.0-alpha.3 (2025-02-11) + + * [9065cd3](https://github.com/tensorchord/envd/commit/9065cd38a05d326f1cd48da9089117dd49bb28c7) fix: julia docs test (#1981) + * [63a0971](https://github.com/tensorchord/envd/commit/63a09712e9d3d2b698b2a2fd9a27617639f693a3) fix: use ubuntu 22.04 mirror (#1980) + * [37cb376](https://github.com/tensorchord/envd/commit/37cb376765bc0fda72d862f9285c1f68388d6e42) fix: use buildkit container builder to export cache (#1975) + +### Contributors + + * Keming + +## v1.0.0-alpha.2 (2025-02-07) + + * [2b3a8d1](https://github.com/tensorchord/envd/commit/2b3a8d184acaaf286c56ada79cd2e3c5bd646c6c) fix: goreleaser config, bump to v6 (#1973) + +### Contributors + + * Keming + +## v1.0.0-alpha.1 (2025-02-07) + + +### Contributors + + +## v1.0.0 (2025-02-11) + + * [9065cd3](https://github.com/tensorchord/envd/commit/9065cd38a05d326f1cd48da9089117dd49bb28c7) fix: julia docs test (#1981) + * [63a0971](https://github.com/tensorchord/envd/commit/63a09712e9d3d2b698b2a2fd9a27617639f693a3) fix: use ubuntu 22.04 mirror (#1980) + * [37cb376](https://github.com/tensorchord/envd/commit/37cb376765bc0fda72d862f9285c1f68388d6e42) fix: use buildkit container builder to export cache (#1975) + * [2b3a8d1](https://github.com/tensorchord/envd/commit/2b3a8d184acaaf286c56ada79cd2e3c5bd646c6c) fix: goreleaser config, bump to v6 (#1973) + * [6b49023](https://github.com/tensorchord/envd/commit/6b49023fcc98d3b426e10595c11610884739ae30) feat: upgrade to micromamba2 (#1971) + * [b53e06d](https://github.com/tensorchord/envd/commit/b53e06d4943c149594dfe1da52043febb51686c5) fix: envd cmd alias conflict, bump buildkit container version (#1970) + * [f747827](https://github.com/tensorchord/envd/commit/f74782795ed92c681516f1b25ec5b9ba5206e346) fix: r-lang in ubuntu:22.04 (#1967) + * [80d77e8](https://github.com/tensorchord/envd/commit/80d77e855203aecb34d0be0f0c9814c5636ab3ff) feat: upgrade julia to latest LTS version (#1966) + * [1519667](https://github.com/tensorchord/envd/commit/15196672f4a04417292a41edfd49cf364ca858de) feat: update examples to v1 syntax (#1965) + * [3bfd29a](https://github.com/tensorchord/envd/commit/3bfd29a18437b9cb7e6b47684be7a7d6da227202) feat: use moby context by default (#1964) + * [e32ab14](https://github.com/tensorchord/envd/commit/e32ab1455150bce2461168791a11ea416d33121f) feat: remove envd v0 (#1963) + * [307fdbd](https://github.com/tensorchord/envd/commit/307fdbda756887e2ba0acc09565965b4d7fe5865) fix: chown all the files for mamba under `/opt/conda` (#1961) + +### Contributors + + * Keming + +## v0.4.3 (2025-01-20) + + * [72e3b49](https://github.com/tensorchord/envd/commit/72e3b495ab776b5ba524217099ed7a2c88cbd909) fix(ci): release on ubuntu with wheels from previous steps (#1960) + +### Contributors + + * Keming + +## v0.4.2 (2025-01-20) + + * [7f31185](https://github.com/tensorchord/envd/commit/7f311854f1103c9bf104bdcc3d2a7fa2a2168b25) feat: update pypi meta, fix macos upload (#1958) + +### Contributors + + * Keming + +## v0.4.1 (2025-01-20) + + * [f6d9fd2](https://github.com/tensorchord/envd/commit/f6d9fd280221de5ad4d0045e7e50755568ed3ff0) feat: use PyPI OIDC for release (#1957) + +### Contributors + + * Keming + +## v0.4.0 (2025-01-20) + + * [600e0df](https://github.com/tensorchord/envd/commit/600e0df522b773076fcc2f89600362e23ad4d223) fix: use ubuntu 22.04 for all the dind related tests (#1955) + * [e737022](https://github.com/tensorchord/envd/commit/e737022c3fe3af01f8bf391feca6e48b08e71753) feat: support fish shell (#1952) + * [1a54052](https://github.com/tensorchord/envd/commit/1a54052e339cb60e2f4efe04ffddd0ffc798af0f) feat: upgrade to ubuntu 22.04 (#1939) + * [5ad6168](https://github.com/tensorchord/envd/commit/5ad61681f6d7e699de69020d498f01e6d818eabe) feat: upgrade to python 3.11 (#1940) + * [1155c9a](https://github.com/tensorchord/envd/commit/1155c9ae53aca62e61584b49cef45b027b4b9557) docs(readme): fix github markdown callout (#1945) + +### Contributors + + * Keming + +## v0.3.47 (2025-01-09) + + * [95bb1c3](https://github.com/tensorchord/envd/commit/95bb1c37f177c6e6e4ba49d4429e6e8a1ebc6ea5) feat(cache): update cuda & cudnn version (#1943) + +### Contributors + + * Keming + +## v0.3.46 (2025-01-08) + + * [da83a62](https://github.com/tensorchord/envd/commit/da83a6268ddc154090d08ef0606f418461823e97) fix(cve): containers/image, fix toto conflicts (#1938) + * [d7ef20d](https://github.com/tensorchord/envd/commit/d7ef20df3444915a2b2793a9d6137caf01f3d3a2) fix(cve): docker & buildkit (#1936) + * [c504c66](https://github.com/tensorchord/envd/commit/c504c66e389c6a3bfc57686b5a47d550f3853125) feat(ci): adopt ruff & lychee (#1935) + * [812de36](https://github.com/tensorchord/envd/commit/812de3622975c7244d7b9b6d1e939b355f35009d) fix(cve): go-get & x/crypto (#1934) + * [d61ca2e](https://github.com/tensorchord/envd/commit/d61ca2e543fd5ec6d8ce238304fb329ee8ed3440) feat: envd up with --gpu-set (#1861) + +### Contributors + + * Keming + * zhang-wei + +## v0.3.45 (2024-01-24) + + * [639c188](https://github.com/tensorchord/envd/commit/639c188536181c1d621bd45d04b2243fef72c467) fix: go-git security issue GHSA-mw99-9chc-xw7r (#1844) + * [457f5fd](https://github.com/tensorchord/envd/commit/457f5fd9a0509f5ef64ce7ea9ce5a2cea601b85e) fix: allow cli --gpus to override config gpu (#1843) + +### Contributors + + * Keming + +## v0.3.44 (2024-01-16) + + * [1f927d7](https://github.com/tensorchord/envd/commit/1f927d7813a687123e637d59c65417b8780088cf) fix: fix comment in /pkg/home/auth.go. (#1831) + * [7b49cc3](https://github.com/tensorchord/envd/commit/7b49cc3748d6579acfeffdd8a92e03b1291582ee) feat(bootstrap): support resume (#1828) + +### Contributors + + * Shao Wang + +## v0.3.43 (2023-12-28) + + * [3394589](https://github.com/tensorchord/envd/commit/3394589f9b57fd12d87661692c73564ba9f10eee) fix: set up python in pypi release step (#1825) + +### Contributors + + * Keming + +## v0.3.42 (2023-12-28) + + * [81b3032](https://github.com/tensorchord/envd/commit/81b30324e297f1d55b26dc82e831255517b11d47) fix: update docker/docker dependency (#1824) + +### Contributors + + * Keming + +## v0.3.41 (2023-12-28) + + * [29788b5](https://github.com/tensorchord/envd/commit/29788b550cc54487f2da81055994bfb216d872f4) feat(log): switch to structured logging(#issue 889) (#1821) + * [4c13224](https://github.com/tensorchord/envd/commit/4c132241ceee866819fed9495b98bf13a42ecae5) fix: indent-error-flow lint (#1795) + * [9027d35](https://github.com/tensorchord/envd/commit/9027d3593b37898dadaaefa2c9a1078611450da6) feat: warn about envd v0 `os` and `image` usage (#1790) + * [bffc025](https://github.com/tensorchord/envd/commit/bffc025da8725e2eab060d079bce2777c6987388) feat: add the reference subcommand to generate the markdown doc (#1766) + +### Contributors + + * Keming + * Shao Wang + +## v0.3.40 (2023-09-01) + + * [d99cfa4](https://github.com/tensorchord/envd/commit/d99cfa423c035cf1361bd22882ccc1a0b2436d64) feat: abort when trying to run `envd up` on non-dev images (#1751) + * [a6613c7](https://github.com/tensorchord/envd/commit/a6613c7c340ee03839a97ba006afaf6653907013) feat: add shm size support (#1746) + +### Contributors + + * Keming + * heyjude + +## v0.3.39 (2023-08-15) + + * [3bcce05](https://github.com/tensorchord/envd/commit/3bcce055b0fd07b442b2d39f4fa9c0e4d2300e2e) feat: inherit the workdir from base image for non-dev env (#1743) + * [0c63598](https://github.com/tensorchord/envd/commit/0c63598f0d932e21df8d236c93d3b4f51d65817a) fix: add ldflags for debug-local (#1741) + +### Contributors + + * Keming + * heyjude + +## v0.3.38 (2023-08-14) + + * [be78326](https://github.com/tensorchord/envd/commit/be783264fdd79ad7262cea0485b7d449ca514f80) feat(v1): copy the requirements content if possible (#1738) + * [66d6d78](https://github.com/tensorchord/envd/commit/66d6d785c8cd810423981a83b8838e98d2c54978) feat(v1): support copy from an image (#1737) + * [7dfd379](https://github.com/tensorchord/envd/commit/7dfd3790b36afde7038b456808bbfd7b187365e2) feat: envd up with `--gpus` (#1730) + +### Contributors + + * Keming + +## v0.3.37 (2023-08-04) + + * [f51990e](https://github.com/tensorchord/envd/commit/f51990e82aaae662d923f9f6a1e6d8bd53c6f7bd) feat: add envd app exit handler (#1722) + * [ddc3009](https://github.com/tensorchord/envd/commit/ddc3009c2bd7739bd1bfd6ac4b1c02892daacbe4) fix: prune bin from py source dist (#1721) + * [af583f4](https://github.com/tensorchord/envd/commit/af583f4db4b842e3c61001981d4aa066ff8d4e63) fix: suppress telemetry send err msg (#1718) + * [b6b70c8](https://github.com/tensorchord/envd/commit/b6b70c8c2fbe6f7b116f0539768abfe165b7b200) fix(v1): chown the whole envd conda env to user envd (#1712) + +### Contributors + + * Keming + +## v0.3.36 (2023-07-18) + + * [854798c](https://github.com/tensorchord/envd/commit/854798c5b368505ea81eb040a5584f39eaee1d68) feat(v1): support moby pushing image (#1708) + +### Contributors + + * Keming + +## v0.3.35 (2023-07-11) + + +### Contributors + + +## v0.3.34 (2023-07-11) + + * [0ff9405](https://github.com/tensorchord/envd/commit/0ff9405d607e1f4a3b1c141c8ea80d5d23e652a7) feat(v1): disable diff/merge op for moby builder (#1699) + +### Contributors + + * Keming + +## v0.3.33 (2023-07-06) + + +### Contributors + + +## v0.3.32 (2023-07-06) + + +### Contributors + + +## v0.3.31 (2023-07-04) + + * [67ce1d7](https://github.com/tensorchord/envd/commit/67ce1d7cd5c234a59294a4bba02df35fa190bb2b) fix: mount buildkitd toml file (#1686) + * [cde4632](https://github.com/tensorchord/envd/commit/cde4632b2954b510e72efe2a7373d2c1d37456e9) support using json for registry config (#1680) + * [b5f9a49](https://github.com/tensorchord/envd/commit/b5f9a49d911ce5ee1e8859968f449835d4147c5f) Fix PATH Order (#1679) + +### Contributors + + * Keming + * Richard Li + +## v0.3.30 (2023-06-29) + + +### Contributors + + +## v0.3.29 (2023-06-29) + + +### Contributors + + +## v0.3.28 (2023-06-29) + + * [88f1541](https://github.com/tensorchord/envd/commit/88f1541c675a5ee50a6b2cedc57ce336e4d6df6f) fix(v1): keep the PATH from the base image (#1673) + * [7379401](https://github.com/tensorchord/envd/commit/73794012482324ed8a909118869631e8401e78f6) feat(v1): inherit the base user for non-dev mode (#1672) + +### Contributors + + * Keming + +## v0.3.27 (2023-06-17) + + * [fa7e989](https://github.com/tensorchord/envd/commit/fa7e989b186149a4f85d39a758bdee7a245b5ebf) refactor: use template for buildkit config file (#1658) + * [45dffe7](https://github.com/tensorchord/envd/commit/45dffe79097b92f59a61391df72f54e15475bb16) feat: support bootstrap buildkit with registry (#1656) + +### Contributors + + * Keming + * Kevin Su + +## v0.3.26 (2023-06-14) + + * [56406e9](https://github.com/tensorchord/envd/commit/56406e980f63acfe27e17738f9eab4a24b1e5565) feat: support bootstrap buildkit with HTTP (#1652) + +### Contributors + + * Keming + +## v0.3.25 (2023-06-13) + + +### Contributors + + +## v0.3.24 (2023-06-08) + + * [edf21bf](https://github.com/tensorchord/envd/commit/edf21bfbe8bfd19f6cdb6be505c0dee0dc851570) fix: authentication for private base image (#1639) + +### Contributors + + * Keming + +## v0.3.23 (2023-05-25) + + * [8a175ed](https://github.com/tensorchord/envd/commit/8a175ed71d59859f57592ddbe73203c4dd8dd6c5) feat: create dest path in io.copy (#1619) + * [7500fa2](https://github.com/tensorchord/envd/commit/7500fa282a582883501c7a70145b4b36f5d2d40b) Documentation improvements (typos, grammar etc) (#1617) + +### Contributors + + * Keming + * Teddy Xinyuan Chen + +## v0.3.22 (2023-05-12) + + * [6569021](https://github.com/tensorchord/envd/commit/656902196da2e847bdfa30999883702f22a7e118) feat: build image by platform (#1582) + * [2839aa3](https://github.com/tensorchord/envd/commit/2839aa33bf097b440064aad9a1aaba461647db50) feat: support mirror ca for buildkitd (#1602) + * [fd45847](https://github.com/tensorchord/envd/commit/fd45847e4ebec79bf644d291fa4d2420ea032373) fix: bootstrap with key pair using the same path for pri/pub key (#1603) + * [2fa41c7](https://github.com/tensorchord/envd/commit/2fa41c7388af41e3ed8de2653d5402ad4bf52369) fix: merge language packages to system packages (#1593) + * [d4df02a](https://github.com/tensorchord/envd/commit/d4df02a4e15e73aff64dfd38cc68ffc2058892f3) fix: In debug model, there is progress display. (#1595) + * [8ab73d8](https://github.com/tensorchord/envd/commit/8ab73d8a7faa9b9fa4a0f882ae904683e2867868) fix(v1): rm bash absolute path (#1594) + +### Contributors + + * Keming + * hang lv + * rrain7 + +## v0.3.21 (2023-05-05) + + * [0a55acd](https://github.com/tensorchord/envd/commit/0a55acd03c6daae2f1de640e4bc751115a8f3564) Use default python in the base image (#1589) + +### Contributors + + * Kevin Su + +## v0.3.20 (2023-05-02) + + * [6e055c6](https://github.com/tensorchord/envd/commit/6e055c61fdc1cdba6959c8a54ead5aad6654090e) feat: inherit entrypoint from the base image (#1587) + * [4a38eca](https://github.com/tensorchord/envd/commit/4a38eca4ddffdb3c24361de90cf2102e7c80fdab) fix: reduce llb merge in language part if not necessary (#1581) + * [d4e5836](https://github.com/tensorchord/envd/commit/d4e58366802efc444c9e0986d42f3836871abe8a) fix: rm the focus in docs v1 test (#1579) + +### Contributors + + * Keming + +## v0.3.19 (2023-04-25) + + * [ad73e37](https://github.com/tensorchord/envd/commit/ad73e37a17a4142c903d9f7bd4434f67708384fa) fix: hard code amd64 to support remote build (#1577) + +### Contributors + + * Ce Gao + +## v0.3.18 (2023-04-23) + + * [e2f9e6b](https://github.com/tensorchord/envd/commit/e2f9e6b180f28735096e8ce70dbe734723fd45c7) feat: confirm `envd destroy` when path and name are both empty (#1568) + * [ac0dd7e](https://github.com/tensorchord/envd/commit/ac0dd7eb0516651bbc175e42870ccdecad86ab93) fix: remove the lock in progressbar (#1569) + * [e56981a](https://github.com/tensorchord/envd/commit/e56981a47ecd1e4f8a7684e1cea6b1720476084f) feat: file sync (#1416) + +### Contributors + + * Alex Xi + * Huan Xu + * Keming + +## v0.3.17 (2023-04-21) + + * [3e426d2](https://github.com/tensorchord/envd/commit/3e426d25d300fad5b3d903ee35aefbbb26f0564b) Download vs code extension by platform (#1566) + * [f3acdff](https://github.com/tensorchord/envd/commit/f3acdff65d68e9315a0575e0d41c3354fb2562bc) fix: compile vscode extensions with user state (#1564) + * [bc0eaf8](https://github.com/tensorchord/envd/commit/bc0eaf86034cd174afa362ecf904c7ea13aec9b6) feat: friendly prompt when exec `envd init` (#1562) + * [e383395](https://github.com/tensorchord/envd/commit/e3833954ef61b1ca1305303565ff1376f5bd1ea2) feat: bring apt-source compilation earlier (#1554) + +### Contributors + + * hang lv + * rrain7 + +## v0.3.16 (2023-03-30) + + * [04f43da](https://github.com/tensorchord/envd/commit/04f43da45423d18feb3a0899ab3589248be7f701) feat: fall back default builder to buildkit-container (#1548) + * [21c3355](https://github.com/tensorchord/envd/commit/21c3355c682476179255ed976cbdb70e9b47a60d) feat(CLI): add fish completion (#1546) + +### Contributors + + * Kaiyang Chen + * zhang-wei + +## v0.3.15 (2023-03-24) + + * [4d62312](https://github.com/tensorchord/envd/commit/4d62312b53db197e6735175eb59e7e58e62dcb60) feat: Support set environment name in `envd up` command (#1495) + * [9a4cad6](https://github.com/tensorchord/envd/commit/9a4cad69d460a1bee4cf4f6056e670a8f121931c) fix(v1): add base env and runtime env to both dev&nondev (#1538) + * [c065935](https://github.com/tensorchord/envd/commit/c065935e1ea053a40efd1543f30000f1c5303bc8) feat: support GitHub merge queue (#1531) + +### Contributors + + * Keming + * Nadeshiko Manju + +## v0.3.14 (2023-03-17) + + * [095811d](https://github.com/tensorchord/envd/commit/095811d63651c121e1e941747569f7f8e264efea) fix: add env to the image itself instead of runtime config (#1528) + * [84b24ee](https://github.com/tensorchord/envd/commit/84b24eec98a86c479e984d4b8352ec249a791600) fix: upgrade buildkit to fix the secury issue (#1526) + * [8c23bad](https://github.com/tensorchord/envd/commit/8c23bad4f349a4024ee658c11d0ac056856e0692) e2e MNIST deep learning example for R (#1510) + * [b641b0c](https://github.com/tensorchord/envd/commit/b641b0c56b9925ee2024093ec6bd7450fca40b4d) feat: Support installing multi-language environment at the same time (#1501) + * [f19289e](https://github.com/tensorchord/envd/commit/f19289e1646ed724e1b32b26c527324c95773e80) misc: Use GitHub Issue Form instead of markdown template (#1494) + * [dc57256](https://github.com/tensorchord/envd/commit/dc57256211e3a7b714ca5185254e66f5a979e02a) feat: add typos to pre-commit and GitHub action (#1493) + +### Contributors + + * Keming + * Nadeshiko Manju + * Zhizhen He + * x0oo0x + +## v0.3.13 (2023-02-20) + + * [53ba50e](https://github.com/tensorchord/envd/commit/53ba50e06fd1af5026a59c88d3886b5c8ca059de) feat: support CPU and memory limit for docker runner (#1486) + * [b0143c3](https://github.com/tensorchord/envd/commit/b0143c34b2d868c5f2e2c7b231b5ededca0f29ea) feat: Allow buildkit timeout to be configurable (#1482) + * [8adb6a9](https://github.com/tensorchord/envd/commit/8adb6a9a0ad7ebea21fd92bdd80883bb71291938) Julia MNIST classification e2e test example (#1484) + * [7d8fc46](https://github.com/tensorchord/envd/commit/7d8fc46a6d311b51ede996ca2d8709d1335780bc) fix: add revive and address indent-error-flow issues (#1480) + +### Contributors + + * Keming + * Yuan Tang + * Zhizhen He + * x0oo0x + +## v0.3.12 (2023-02-14) + + * [0929661](https://github.com/tensorchord/envd/commit/09296619283ce568866df414bf26db3bda24d9c7) feat: support buildx moby worker in docker 23.0.0 onward to accelerating building process by skipping docker load (#1472) + * [986c52a](https://github.com/tensorchord/envd/commit/986c52a2b9ccec53a688de9bd03bd5cc880129bf) fix: unify receiver type for generalManager (#1473) + * [ae3a1e5](https://github.com/tensorchord/envd/commit/ae3a1e5680bffa214e22f0ef3d8a5b10b6be824a) fix: pass loop variable as a function parameter (#1470) + * [23e4fa9](https://github.com/tensorchord/envd/commit/23e4fa9a552aecab43566504f6c66ede5f57d6b2) feat: add progress bar for `envd up` and `envd run` (#1460) + +### Contributors + + * Kaiyang Chen + * Keming + * Zhizhen He + +## v0.3.11 (2023-02-01) + + * [89383b5](https://github.com/tensorchord/envd/commit/89383b5bba5229e9cb133515b69a1eccdd0331da) fix: rm redundant ForwardAgent config in ssh config (#1458) + * [b9f20a4](https://github.com/tensorchord/envd/commit/b9f20a4bfa28bf4bf0cb4ae90c3ba95e5ef23bb1) feat: separate starship installation by using fixed image (#1424) + * [29ffd89](https://github.com/tensorchord/envd/commit/29ffd895601fad818d5625a03d3e9034d2103ec7) feat: support envd run in local docker context (#1428) + +### Contributors + + * Jinjing Zhou + * Kaiyang Chen + * Keming + +## v0.3.10 (2023-01-25) + + * [efdd05f](https://github.com/tensorchord/envd/commit/efdd05f5219c92a5cbae14a57fb55199535b9cd4) fix: rm remote cache for v1 (#1447) + * [f940354](https://github.com/tensorchord/envd/commit/f9403540e1efe08b8512a728f9cd0eb3232dfb76) feat: add shm size to start options (#1445) + * [83c63c7](https://github.com/tensorchord/envd/commit/83c63c7dbce98cd2db53fc317a5366d78ecb5bfd) fix: pip install requirements.txt (#1434) + * [f47f7a3](https://github.com/tensorchord/envd/commit/f47f7a353cd84dc03f577116bac33090ab8f0eac) doc: add init and daemon debug guide (#1435) + * [b6d49c6](https://github.com/tensorchord/envd/commit/b6d49c64290e32b4c92be009f40371b296899573) feat: add `make` as a default system package (#1433) + * [686d029](https://github.com/tensorchord/envd/commit/686d0295b36b4b8227c65b8e8aee60bd062006f6) fix: Add docker auth (#1432) + * [2d03de9](https://github.com/tensorchord/envd/commit/2d03de9872610b1177f63b613c0f38d36a9eed19) feat: :sparkles: init nerdctl support (#1378) + +### Contributors + + * Ce Gao + * Keming + * Wei Zhang + ## v0.3.9 (2023-01-18) @@ -18,9 +506,7 @@ * [0f556d0](https://github.com/tensorchord/envd/commit/0f556d0b630df70c17b98f5444ba3dcd67abd0bd) fix: change default channel to `defaults` (#1427) * [949f188](https://github.com/tensorchord/envd/commit/949f188a074fffef300cc4489806317357931d0b) LLM inference example (#1425) - * [46f5996](https://github.com/tensorchord/envd/commit/46f59963dfe10c369b21320f754b20721cd085c1) chore(deps): bump github.com/tensorchord/envd-server from 0.0.23 to 0.0.24 (#1388) * [b93cd48](https://github.com/tensorchord/envd/commit/b93cd48b1e87642677d1a34120427b7b28192d90) feat: add checksum check for Julia archive file (#1419) - * [c87846e](https://github.com/tensorchord/envd/commit/c87846e68f2eb0220e0c8698e94894c82fb04491) chore(deps): bump github.com/onsi/ginkgo/v2 from 2.6.1 to 2.7.0 (#1418) * [64b5a0a](https://github.com/tensorchord/envd/commit/64b5a0ab5f7799ba3210bf0afa1c5521d078d963) Optimize the logic of adding entry in wsl (#1414) * [e1bbd56](https://github.com/tensorchord/envd/commit/e1bbd565b266e785ebb05b0e6227161f12bb8b14) feat: support trust the pypi index in v1, add api doc (#1412) * [d1179a6](https://github.com/tensorchord/envd/commit/d1179a6cbaece9d5627fa76dd8a1d464cb253f0e) fix: v1 gid on darwin (#1411) @@ -31,7 +517,6 @@ * Jinjing Zhou * Keming - * dependabot[bot] * li mengyang * x0oo0x @@ -44,7 +529,6 @@ * [b4c08c8](https://github.com/tensorchord/envd/commit/b4c08c835b427a426d20681c4e6093e4ce3c786d) Initial e2e test for R language environment (#1342) * [3f7d699](https://github.com/tensorchord/envd/commit/3f7d699c9dfc36cb85433a8796d745774a91f1db) Refactor R package installation (#1381) * [aab7f53](https://github.com/tensorchord/envd/commit/aab7f53d2cf57549598c73040d98165fb2ecf5cb) feat: `envd exec` get runtime graph from container labels (#1392) - * [ede2013](https://github.com/tensorchord/envd/commit/ede201335b95d5071224f9aeeb03d81830472582) chore(deps): bump golang.org/x/term from 0.3.0 to 0.4.0 (#1389) * [7034c5d](https://github.com/tensorchord/envd/commit/7034c5dade81293572fdb604d0c323461a13772b) Revert "fix: increase the default buildkit cache limit" (#1386) * [f56d95a](https://github.com/tensorchord/envd/commit/f56d95a2eb66bef6044d5653983f714cd0df6da3) fix: increase the default buildkit cache limit (#1382) * [3c38b5e](https://github.com/tensorchord/envd/commit/3c38b5e62753d51ec470586b44ada1b14af6eb64) fix: v0 user passwd, add test for root permission (#1379) @@ -57,7 +541,6 @@ * Ce Gao * Keming * Weixiao Huang - * dependabot[bot] * x0oo0x ## v0.3.5 (2023-01-03) @@ -85,15 +568,12 @@ ## v0.3.2 (2023-01-03) * [095237d](https://github.com/tensorchord/envd/commit/095237dee602eb76dedf9041370173eee367d467) fix: fix nil pointer dereference when exiting shell (#1364) - * [aae5e27](https://github.com/tensorchord/envd/commit/aae5e27feaa06436fa81c3dc414b28b6b7e21a3b) chore(deps): bump github.com/mattn/go-isatty from 0.0.16 to 0.0.17 (#1361) * [b0b5d71](https://github.com/tensorchord/envd/commit/b0b5d71ccb463a7e26e6f3f0cf2ce5d094731367) fix: remove duplicated config and fix Dockerfile pattern (#1362) * [bb0fe7c](https://github.com/tensorchord/envd/commit/bb0fe7cac7d6194f2fe095a5aa61bf68a96a564d) Fix: Remove unnecessary user switching code when installing R package (#1351) * [4f406ce](https://github.com/tensorchord/envd/commit/4f406ce057c36e1c45ee1e5d2b4c27a8120796ca) fix: fix broken link (#1349) * [3093e1a](https://github.com/tensorchord/envd/commit/3093e1a4c64ff1cbff58f656124951d561445a73) fix: v1 change default user group to 1000:1000 (#1347) * [571abfe](https://github.com/tensorchord/envd/commit/571abfede9e2166ba5fbefaaf046a4ff926309ad) feat: add --format for commands to output json (#1323) * [296680c](https://github.com/tensorchord/envd/commit/296680ce6a02d5a0605936a49de250ba236baaa5) fix: install black[jupyter] for CI check (#1343) - * [cb33a65](https://github.com/tensorchord/envd/commit/cb33a656801a2f6b6cd04f4b4631ff17b3a2d3a8) chore(deps): bump github.com/tensorchord/envd-server from 0.0.21 to 0.0.23 (#1341) - * [722fca1](https://github.com/tensorchord/envd/commit/722fca1d11a5519a79f6be29ca48ef67a2414b55) chore(deps): bump pypa/cibuildwheel from 2.11.3 to 2.11.4 (#1340) * [7426973](https://github.com/tensorchord/envd/commit/7426973cc33dd56a3967a4c2163575a92359a963) doc: add pytorch2 example (#1339) * [e1cb495](https://github.com/tensorchord/envd/commit/e1cb4953d008eb3ed0816e667a59595da9204a3c) fix: doc url in envd github issue page (#1338) * [122021d](https://github.com/tensorchord/envd/commit/122021da173e62ddbada683ddba4dfd8eb0432cc) refact: change Shlex(fmt.Sprintf to Shlexf, use ` instead of escape \" (#1337) @@ -104,7 +584,6 @@ * Keming * Zhizhen He * cutecutecat - * dependabot[bot] * x0oo0x * xing0821 * xxchan @@ -115,17 +594,14 @@ * [49b3236](https://github.com/tensorchord/envd/commit/49b323609c52d6550b150e5cb44dc941d480d0d0) feat(login): Refactor logic with the new key API (#1333) * [1cd917d](https://github.com/tensorchord/envd/commit/1cd917dbe3b1fede67cfe9dd216e86622790b007) fix: upgrade horust to support arm (#1330) * [d39adc8](https://github.com/tensorchord/envd/commit/d39adc851a8e967e56221f4ac09775fc09f686af) refactor: combine code with the same logic (#1326) - * [d1990ec](https://github.com/tensorchord/envd/commit/d1990ec5b8677d9c588919c887c5a304b01b3703) chore(deps): bump github.com/onsi/gomega from 1.24.1 to 1.24.2 (#1320) * [c598834](https://github.com/tensorchord/envd/commit/c598834ee34abc8675e19d2fb31bacbcdaa1acdb) fix: conda meta permission for envd user (#1325) * [0a48acd](https://github.com/tensorchord/envd/commit/0a48acd355bfe6c62380f7729991993063ca2991) feat: produce ABI-agnostic wheels for python (#1324) - * [8042997](https://github.com/tensorchord/envd/commit/80429972639cf57d8127a7d5bd8d71ad41834d89) chore(deps): bump github.com/onsi/ginkgo/v2 from 2.6.0 to 2.6.1 (#1321) ### Contributors * Ce Gao * Frost Ming * Keming - * dependabot[bot] * rrain7 * x0oo0x @@ -152,10 +628,6 @@ * [935e55d](https://github.com/tensorchord/envd/commit/935e55d5907378ab967d4c767bff7ec3b0c0909a) Interactive cli (#1294) * [3800ea2](https://github.com/tensorchord/envd/commit/3800ea2971ea260431fd8fc5ab22ad9b069d8251) fix: add PATH when switch to envd user (#1295) * [382a6c1](https://github.com/tensorchord/envd/commit/382a6c150e30ea883c3229b37d851d85ead737bc) feat: install pypi packages in different groups (#1289) - * [c66f201](https://github.com/tensorchord/envd/commit/c66f201c90a3ffee039a13679c41e792a1ebeac5) chore(deps): bump github.com/onsi/ginkgo/v2 from 2.5.1 to 2.6.0 (#1293) - * [d15c3ca](https://github.com/tensorchord/envd/commit/d15c3caf450009c4e94172b45474d8e268ac76e8) chore(deps): bump golang.org/x/term from 0.2.0 to 0.3.0 (#1292) - * [50a8bb6](https://github.com/tensorchord/envd/commit/50a8bb620c178adaa80ce59edea364759afbd5bc) chore(deps): bump github.com/urfave/cli/v2 from 2.23.6 to 2.23.7 (#1291) - * [99e8a15](https://github.com/tensorchord/envd/commit/99e8a15fd240d83810e2f4414bcb66d3addcb355) chore(deps): bump pypa/cibuildwheel from 2.11.2 to 2.11.3 (#1290) * [f7f2b34](https://github.com/tensorchord/envd/commit/f7f2b34a80f616ae4db1c0fdb1c01f6583f4821f) Add config.owner to v1 syntax for setting UID and GID (#1286) * [5c4aae9](https://github.com/tensorchord/envd/commit/5c4aae93f797a6ce504f3d09f6395e5492627703) fix: Remove graph access in ssh (#1281) * [56d25a0](https://github.com/tensorchord/envd/commit/56d25a01dd37b2c069ae9b4492811c8571a1e714) feat: Support specify resource requirement when creating the environment (#1268) @@ -164,8 +636,6 @@ * [8651444](https://github.com/tensorchord/envd/commit/865144482b380cf5d3d6d30dff55a45087aeabc4) Remove incorrect `sudo` command when installing system package (#1273) * [235dde9](https://github.com/tensorchord/envd/commit/235dde905643e2eed7ba91e10423b92598d6a12a) fix: Remove ir direct access in ssh (#1271) * [06b4880](https://github.com/tensorchord/envd/commit/06b488035a5c21a74c8727fc7d8e85f404a2bb00) feat: Add JWT and user/pwd in envd server context (#1243) - * [7badc67](https://github.com/tensorchord/envd/commit/7badc67fcf9be813d2124aed7f18acd855847e63) chore(deps): bump github.com/tensorchord/envd-server from 0.0.11 to 0.0.12 (#1266) - * [cefd1df](https://github.com/tensorchord/envd/commit/cefd1dfa8101a363a762921ec7439f64d1463e2a) chore(deps): bump github.com/urfave/cli/v2 from 2.23.5 to 2.23.6 (#1264) ### Contributors @@ -174,7 +644,6 @@ * Frost Ming * Jinjing Zhou * Keming - * dependabot[bot] * nullday * x0oo0x @@ -224,18 +693,14 @@ ## v0.2.5-rc.1 (2022-11-28) - * [a4dad9b](https://github.com/tensorchord/envd/commit/a4dad9b212c41e07175f201e067f891c76277843) chore(deps): bump github.com/tensorchord/envd-server from 0.0.9 to 0.0.10 (#1235) ### Contributors - * dependabot[bot] ## v0.2.5-alpha.8 (2022-11-23) * [2f37901](https://github.com/tensorchord/envd/commit/2f379016002e58c3a87b2a8b2e2afd6ffc6ab9b9) feat: support add new dir to runtime PATH (#1218) * [e85f062](https://github.com/tensorchord/envd/commit/e85f0620ad76379e22fdf36f70697b1f8d71444b) feat: merge coverage file and report to goverall (#1211) - * [7f10440](https://github.com/tensorchord/envd/commit/7f10440a8163f4d23964783fdf83c2e03c66e4f9) chore(deps): bump github.com/onsi/ginkgo/v2 from 2.5.0 to 2.5.1 (#1213) - * [6db3f15](https://github.com/tensorchord/envd/commit/6db3f152325b674ea67e24fc4ee694f99ea206c3) chore(deps): bump github.com/tensorchord/envd-server from 0.0.8 to 0.0.9 (#1212) * [5efb0b1](https://github.com/tensorchord/envd/commit/5efb0b12b98e3000d0ddc6ef015734e65411ac80) fix: only mkdir /home/envd/* when image==nil (#1208) * [8c8c3f3](https://github.com/tensorchord/envd/commit/8c8c3f33ada10013b350071a1a36529d993053c4) feat: add name to `envd envs describe` (#1201) @@ -244,7 +709,6 @@ * Alex Xi * Keming * cutecutecat - * dependabot[bot] ## v0.2.5-alpha.7 (2022-11-15) @@ -280,18 +744,11 @@ ## v0.2.5-alpha.3 (2022-11-14) - * [212f996](https://github.com/tensorchord/envd/commit/212f9966d12f2322fe8c97d14db093448399119e) chore(deps): bump github.com/urfave/cli/v2 from 2.23.4 to 2.23.5 (#1188) - * [c4854e8](https://github.com/tensorchord/envd/commit/c4854e842a7cee0f2600f5c6eb8d19ca3510369d) chore(deps): bump github.com/onsi/gomega from 1.24.0 to 1.24.1 (#1189) - * [62b7b2f](https://github.com/tensorchord/envd/commit/62b7b2f204aa5b53688a2b0356d8a746a5fac1e4) chore(deps): bump github.com/moby/buildkit from 0.10.5 to 0.10.6 (#1190) * [b1eb44e](https://github.com/tensorchord/envd/commit/b1eb44e4cc80b3422122429d3f29e5d7ffcaf3d6) fix: pre-create the workdir (#1174) * [66d9cb0](https://github.com/tensorchord/envd/commit/66d9cb061a033220814813a349fc12495927aef2) fix: Move user to image config (#1173) * [dd9dd30](https://github.com/tensorchord/envd/commit/dd9dd3083b363d1d698060255892e6a132b8182a) refact: move env to server (#1172) * [2d37b48](https://github.com/tensorchord/envd/commit/2d37b48736c5ba34ea0800c925c31c0c3ff40f13) fix stable diffusion demo via b/c huggingface api change (#1170) * [c364958](https://github.com/tensorchord/envd/commit/c364958c1d21d25a7cb782413f6666bd666f7dd1) feat: support envd-server image (#1150) - * [f9e7c67](https://github.com/tensorchord/envd/commit/f9e7c67a0f16c802fa2aa75a562f77e6f17baef9) chore(deps): bump github.com/spf13/viper from 1.13.0 to 1.14.0 (#1164) - * [5c9ac69](https://github.com/tensorchord/envd/commit/5c9ac69a584463cf906b27d0ccd200403d7f4974) chore(deps): bump github.com/onsi/gomega from 1.23.0 to 1.24.0 (#1163) - * [d1de890](https://github.com/tensorchord/envd/commit/d1de8906326ee10a1021dbff80892d8f0c74e637) chore(deps): bump github.com/urfave/cli/v2 from 2.23.0 to 2.23.4 (#1162) - * [ad5ce11](https://github.com/tensorchord/envd/commit/ad5ce117bdc4433f8894c4b813748d33c6edbd96) chore(deps): bump dependabot/fetch-metadata from 1.3.4 to 1.3.5 (#1161) * [6b7a0e9](https://github.com/tensorchord/envd/commit/6b7a0e94937d5e0532139d519e424ccb16bd9de1) fix: cuda tag in image cache (#1156) * [1af3449](https://github.com/tensorchord/envd/commit/1af3449ce72e218350c3d4fa19aee64f98ced970) fix: pre mkdir for runtime mount (#1153) @@ -299,7 +756,6 @@ * Ce Gao * Keming - * dependabot[bot] * xieydd ## v0.2.5-alpha.2 (2022-11-03) @@ -320,35 +776,23 @@ * [235dde9](https://github.com/tensorchord/envd/commit/235dde905643e2eed7ba91e10423b92598d6a12a) fix: Remove ir direct access in ssh (#1271) * [06b4880](https://github.com/tensorchord/envd/commit/06b488035a5c21a74c8727fc7d8e85f404a2bb00) feat: Add JWT and user/pwd in envd server context (#1243) - * [7badc67](https://github.com/tensorchord/envd/commit/7badc67fcf9be813d2124aed7f18acd855847e63) chore(deps): bump github.com/tensorchord/envd-server from 0.0.11 to 0.0.12 (#1266) - * [cefd1df](https://github.com/tensorchord/envd/commit/cefd1dfa8101a363a762921ec7439f64d1463e2a) chore(deps): bump github.com/urfave/cli/v2 from 2.23.5 to 2.23.6 (#1264) * [8594d10](https://github.com/tensorchord/envd/commit/8594d1044874ed2baa3bc74ef9b5e35f3766a6f7) fix: Update python setuptools to python3 (#1261) * [0042b24](https://github.com/tensorchord/envd/commit/0042b2481da98c6810e858069f769f3b7ef4808a) feat: add completion command (#1258) * [5cbed88](https://github.com/tensorchord/envd/commit/5cbed887e0aa6b0c766f48c0cf8effe85f49d20b) fix: put the binary under bin directly (#1254) * [c7fd12b](https://github.com/tensorchord/envd/commit/c7fd12b9047b5c59e555a3d083cdf7cdef1449d6) bug: Update envd-server version to 0.0.11 (#1245) - * [a4dad9b](https://github.com/tensorchord/envd/commit/a4dad9b212c41e07175f201e067f891c76277843) chore(deps): bump github.com/tensorchord/envd-server from 0.0.9 to 0.0.10 (#1235) * [2f37901](https://github.com/tensorchord/envd/commit/2f379016002e58c3a87b2a8b2e2afd6ffc6ab9b9) feat: support add new dir to runtime PATH (#1218) * [e85f062](https://github.com/tensorchord/envd/commit/e85f0620ad76379e22fdf36f70697b1f8d71444b) feat: merge coverage file and report to goverall (#1211) - * [7f10440](https://github.com/tensorchord/envd/commit/7f10440a8163f4d23964783fdf83c2e03c66e4f9) chore(deps): bump github.com/onsi/ginkgo/v2 from 2.5.0 to 2.5.1 (#1213) - * [6db3f15](https://github.com/tensorchord/envd/commit/6db3f152325b674ea67e24fc4ee694f99ea206c3) chore(deps): bump github.com/tensorchord/envd-server from 0.0.8 to 0.0.9 (#1212) * [5efb0b1](https://github.com/tensorchord/envd/commit/5efb0b12b98e3000d0ddc6ef015734e65411ac80) fix: only mkdir /home/envd/* when image==nil (#1208) * [8c8c3f3](https://github.com/tensorchord/envd/commit/8c8c3f33ada10013b350071a1a36529d993053c4) feat: add name to `envd envs describe` (#1201) * [3fa3070](https://github.com/tensorchord/envd/commit/3fa307098a585b524804bd0beff6697e68440b42) feat: support build owner (#1202) * [5462d30](https://github.com/tensorchord/envd/commit/5462d30a556f91741b770e4b4b24c1fd639f5299) fix: release CI add runneradmin to docker group (#1199) * [731b6f8](https://github.com/tensorchord/envd/commit/731b6f8271a77096d9a29a03e353e0388f825485) fix: github action release cache command (#1197) * [4a7d6cc](https://github.com/tensorchord/envd/commit/4a7d6cca18d9a033c02c1ef235570f64ef987300) fix: daemon command (#1185) - * [212f996](https://github.com/tensorchord/envd/commit/212f9966d12f2322fe8c97d14db093448399119e) chore(deps): bump github.com/urfave/cli/v2 from 2.23.4 to 2.23.5 (#1188) - * [c4854e8](https://github.com/tensorchord/envd/commit/c4854e842a7cee0f2600f5c6eb8d19ca3510369d) chore(deps): bump github.com/onsi/gomega from 1.24.0 to 1.24.1 (#1189) - * [62b7b2f](https://github.com/tensorchord/envd/commit/62b7b2f204aa5b53688a2b0356d8a746a5fac1e4) chore(deps): bump github.com/moby/buildkit from 0.10.5 to 0.10.6 (#1190) * [b1eb44e](https://github.com/tensorchord/envd/commit/b1eb44e4cc80b3422122429d3f29e5d7ffcaf3d6) fix: pre-create the workdir (#1174) * [66d9cb0](https://github.com/tensorchord/envd/commit/66d9cb061a033220814813a349fc12495927aef2) fix: Move user to image config (#1173) * [dd9dd30](https://github.com/tensorchord/envd/commit/dd9dd3083b363d1d698060255892e6a132b8182a) refact: move env to server (#1172) * [2d37b48](https://github.com/tensorchord/envd/commit/2d37b48736c5ba34ea0800c925c31c0c3ff40f13) fix stable diffusion demo via b/c huggingface api change (#1170) * [c364958](https://github.com/tensorchord/envd/commit/c364958c1d21d25a7cb782413f6666bd666f7dd1) feat: support envd-server image (#1150) - * [f9e7c67](https://github.com/tensorchord/envd/commit/f9e7c67a0f16c802fa2aa75a562f77e6f17baef9) chore(deps): bump github.com/spf13/viper from 1.13.0 to 1.14.0 (#1164) - * [5c9ac69](https://github.com/tensorchord/envd/commit/5c9ac69a584463cf906b27d0ccd200403d7f4974) chore(deps): bump github.com/onsi/gomega from 1.23.0 to 1.24.0 (#1163) - * [d1de890](https://github.com/tensorchord/envd/commit/d1de8906326ee10a1021dbff80892d8f0c74e637) chore(deps): bump github.com/urfave/cli/v2 from 2.23.0 to 2.23.4 (#1162) - * [ad5ce11](https://github.com/tensorchord/envd/commit/ad5ce117bdc4433f8894c4b813748d33c6edbd96) chore(deps): bump dependabot/fetch-metadata from 1.3.4 to 1.3.5 (#1161) * [6b7a0e9](https://github.com/tensorchord/envd/commit/6b7a0e94937d5e0532139d519e424ccb16bd9de1) fix: cuda tag in image cache (#1156) * [1af3449](https://github.com/tensorchord/envd/commit/1af3449ce72e218350c3d4fa19aee64f98ced970) fix: pre mkdir for runtime mount (#1153) * [46d24fd](https://github.com/tensorchord/envd/commit/46d24fd1331b1b0e223cb2d2223e17d648080bf1) fix: horust cache (#1151) @@ -363,9 +807,6 @@ * [0bdca3c](https://github.com/tensorchord/envd/commit/0bdca3c2d54d0eea8ee6b3a14641ae5563971b0e) fix: Remove cwd in conda to enable remote cache (#1125) * [535e26a](https://github.com/tensorchord/envd/commit/535e26a0a0d9d3855317d34dcf209e4d8d08efe9) feat(debug): Add llb export (#1124) * [d693254](https://github.com/tensorchord/envd/commit/d693254c1a8b19e02aca85934818a4e7b6dde661) fix: Disable telemetry in CI (#1116) - * [3cba45e](https://github.com/tensorchord/envd/commit/3cba45e36496cd6010d9b07acbb0c7d34826f828) chore(deps): bump github.com/urfave/cli/v2 from 2.20.3 to 2.23.0 (#1119) - * [57a63e0](https://github.com/tensorchord/envd/commit/57a63e015c813a0a446768b00c83039923ce0df3) chore(deps): bump pypa/cibuildwheel from 2.11.1 to 2.11.2 (#1115) - * [77b3d84](https://github.com/tensorchord/envd/commit/77b3d84741f990e9cf0b3a8f9bd56abe40fcd895) chore(deps): bump github.com/onsi/gomega from 1.22.1 to 1.23.0 (#1117) ### Contributors @@ -376,7 +817,6 @@ * Jinjing Zhou * Keming * cutecutecat - * dependabot[bot] * nullday * tison * xieydd @@ -408,11 +848,6 @@ * [753fde3](https://github.com/tensorchord/envd/commit/753fde329e7da22837af27cddb48969947b9125b) fix:#1090 (#1092) * [1ddd513](https://github.com/tensorchord/envd/commit/1ddd513c03b78b8382d309c3a9fb162ae39973b9) feat(CLI): Support envd-server in up command (#1087) * [ab9fe2c](https://github.com/tensorchord/envd/commit/ab9fe2ce255ea18780310f37903b216853386589) feat: Support forward (#1014) - * [4cd6fef](https://github.com/tensorchord/envd/commit/4cd6fef5d6284538ea523011aac47feec8dcf36a) chore(deps): bump github.com/moby/buildkit from 0.10.4 to 0.10.5 (#1081) - * [a81bbb0](https://github.com/tensorchord/envd/commit/a81bbb0aab9b1a7d1bf37a0374884f34af386c3b) chore(deps): bump github.com/urfave/cli/v2 from 2.20.2 to 2.20.3 (#1078) - * [a89f466](https://github.com/tensorchord/envd/commit/a89f466517aec054c0e0c5a295f09b5e7b3c33b7) chore(deps): bump github.com/opencontainers/image-spec from 1.1.0-rc1 to 1.1.0-rc2 (#1077) - * [a209f79](https://github.com/tensorchord/envd/commit/a209f79d07b945c2a1a87972f191be7f4428d6c1) chore(deps): bump github.com/onsi/gomega from 1.21.1 to 1.22.1 (#1080) - * [a8a6339](https://github.com/tensorchord/envd/commit/a8a633928a5ec70f5deccbebf81619bb1dbe33da) chore(deps): bump github.com/stretchr/testify from 1.8.0 to 1.8.1 (#1079) ### Contributors @@ -420,7 +855,6 @@ * Friends A * Jinjing Zhou * Yilong Li - * dependabot[bot] ## v0.2.4-alpha.15 (2022-10-21) @@ -467,11 +901,6 @@ * [cf78dd1](https://github.com/tensorchord/envd/commit/cf78dd131e869ad20fe67a4a1159d7bf69e6fb63) feat: Implement env client in envd engine (#1049) * [bc6255b](https://github.com/tensorchord/envd/commit/bc6255bc3d494be85afc7c132f2e48f72fffd0fa) example: Add torch profiler example (#1026) * [ded3fce](https://github.com/tensorchord/envd/commit/ded3fce1b481aab9c51c2f13dbb2d92288532da0) feat: add `envd down` as an alias for `envd destroy` (#1047) - * [9b8582d](https://github.com/tensorchord/envd/commit/9b8582d910e83288ef90ba45b599221341ad29f6) chore(deps): bump github.com/onsi/ginkgo/v2 from 2.2.0 to 2.3.1 (#1039) - * [0ee19ed](https://github.com/tensorchord/envd/commit/0ee19ed79d3332a25082878ca19c1bc032072aef) chore(deps): bump pypa/cibuildwheel from 2.10.2 to 2.11.1 (#1036) - * [6ba7633](https://github.com/tensorchord/envd/commit/6ba763368d81035b0f13e5eb1549856ecaaa30d1) chore(deps): bump k8s.io/api from 0.25.2 to 0.25.3 (#1038) - * [0500dca](https://github.com/tensorchord/envd/commit/0500dca9b5174510c1c741ec1d9cfbf97d09a2e9) chore(deps): bump github.com/urfave/cli/v2 from 2.19.2 to 2.20.2 (#1037) - * [847f187](https://github.com/tensorchord/envd/commit/847f187b45983a56093dd962d5638dae5f260743) chore(deps): bump github.com/onsi/gomega from 1.21.1 to 1.22.1 (#1040) * [1fb2721](https://github.com/tensorchord/envd/commit/1fb272197f405c5ee9dcca46c82079095afa406d) bug: Fix flaky auth test (#1034) * [d0134d3](https://github.com/tensorchord/envd/commit/d0134d301eba153dcda0c79a3a23babf75a14434) fix(zsh): ignore inserting zsh-completion if system don't have zsh shell (#1025) * [ec92a29](https://github.com/tensorchord/envd/commit/ec92a295b835664d559f201ba2542a6a609a560d) feat: support using current directory name as env name in envd describe (#1033) @@ -481,7 +910,6 @@ * Ce Gao * Jinjing Zhou * Zhenzhen Zhao - * dependabot[bot] * wangxiaolei ## v0.2.4-alpha.10 (2022-10-14) @@ -498,9 +926,7 @@ * [46168da](https://github.com/tensorchord/envd/commit/46168dac70fb8cd9b841a32e35a414ad29ddfaa2) chore(test): Add test cases for pkg/home/auth.go (#1009) * [d87afda](https://github.com/tensorchord/envd/commit/d87afda9213fcb334ca55d247ba3c0663b7b180b) feat: Add envd-server runtime proposal (#303) * [fec7ada](https://github.com/tensorchord/envd/commit/fec7ada4efc2186fa5f858d5abe211a2beb2dfdd) fix: Remove hard code docker in envd engine init (#1000) - * [cf56f0d](https://github.com/tensorchord/envd/commit/cf56f0de7bfb05be47d4704c14f9bbb892a86d28) chore(deps): bump github.com/onsi/gomega from 1.20.2 to 1.21.1 (#997) * [a55470e](https://github.com/tensorchord/envd/commit/a55470e363b35b689649596b449b996ada701630) feat(build): detect if the current environment is running before building (#892) (#989) - * [d0219f2](https://github.com/tensorchord/envd/commit/d0219f2e3e999e27f334a39180031390a4554af9) chore(deps): bump github.com/urfave/cli/v2 from 2.17.1 to 2.19.2 (#998) ### Contributors @@ -511,7 +937,6 @@ * XRW * Yijiang Liu * Zhenzhen Zhao - * dependabot[bot] ## v0.2.4-alpha.9 (2022-10-09) @@ -531,8 +956,6 @@ * [17fedb8](https://github.com/tensorchord/envd/commit/17fedb8d2ae41318e2f705687c334b7e42413ab3) fix(ir): make sure default value won't be replaced with empty value (#970) * [c1ae887](https://github.com/tensorchord/envd/commit/c1ae887f12b23802363ac417c198e19983c5605f) feat(CLI): Support runner in context (#961) * [bf993e2](https://github.com/tensorchord/envd/commit/bf993e2cab44502abb74a79e732b74a5567b6194) refact: conda/mamba create/update env, fix user permissions (#933) - * [0e79fb9](https://github.com/tensorchord/envd/commit/0e79fb91b9f3770f2e5356fc8a10182dd31e2bdf) chore(deps): bump github.com/urfave/cli/v2 from 2.16.3 to 2.17.1 (#968) - * [c9045d2](https://github.com/tensorchord/envd/commit/c9045d24d412099a1ac0bf9b9acf78135157351d) chore(deps): bump dependabot/fetch-metadata from 1.3.3 to 1.3.4 (#967) ### Contributors @@ -540,7 +963,6 @@ * Keming * Tumushimire Yves * Weizhen Wang - * dependabot[bot] ## v0.2.4-alpha.7 (2022-10-01) @@ -587,14 +1009,12 @@ ## v0.2.4-alpha.2 (2022-09-28) * [b704029](https://github.com/tensorchord/envd/commit/b70402917edf874a0a8e630664b637fa8c22cd53) feat(ir): all in llb (#941) - * [f5f70e0](https://github.com/tensorchord/envd/commit/f5f70e0de304b8ff4767cd935ab3d307ed5599a2) chore(deps): bump pypa/cibuildwheel from 2.10.1 to 2.10.2 (#936) * [bd69c3d](https://github.com/tensorchord/envd/commit/bd69c3df326f1f213472960c099dff0c6d35e41c) feat: Support envd-server (#932) ### Contributors * Ce Gao * Keming - * dependabot[bot] ## v0.2.4-alpha.1 (2022-09-21) @@ -605,9 +1025,6 @@ ## v0.2.4 (2022-10-31) * [d693254](https://github.com/tensorchord/envd/commit/d693254c1a8b19e02aca85934818a4e7b6dde661) fix: Disable telemetry in CI (#1116) - * [3cba45e](https://github.com/tensorchord/envd/commit/3cba45e36496cd6010d9b07acbb0c7d34826f828) chore(deps): bump github.com/urfave/cli/v2 from 2.20.3 to 2.23.0 (#1119) - * [57a63e0](https://github.com/tensorchord/envd/commit/57a63e015c813a0a446768b00c83039923ce0df3) chore(deps): bump pypa/cibuildwheel from 2.11.1 to 2.11.2 (#1115) - * [77b3d84](https://github.com/tensorchord/envd/commit/77b3d84741f990e9cf0b3a8f9bd56abe40fcd895) chore(deps): bump github.com/onsi/gomega from 1.22.1 to 1.23.0 (#1117) * [197b6c6](https://github.com/tensorchord/envd/commit/197b6c612891f0b7bc95b4b1f12a7daacbe7e51f) feat: Add telemetry with the help of segment.io (#1113) * [dedd731](https://github.com/tensorchord/envd/commit/dedd73113214d68fd6f8421446fcf2ea8895252e) feat: Add listening_addr to expose fun (#1110) * [f672c8f](https://github.com/tensorchord/envd/commit/f672c8f67c9fd793427fb9ec556e767fdf1ef50b) feat: support build time run without mount host (#1109) @@ -622,11 +1039,6 @@ * [753fde3](https://github.com/tensorchord/envd/commit/753fde329e7da22837af27cddb48969947b9125b) fix:#1090 (#1092) * [1ddd513](https://github.com/tensorchord/envd/commit/1ddd513c03b78b8382d309c3a9fb162ae39973b9) feat(CLI): Support envd-server in up command (#1087) * [ab9fe2c](https://github.com/tensorchord/envd/commit/ab9fe2ce255ea18780310f37903b216853386589) feat: Support forward (#1014) - * [4cd6fef](https://github.com/tensorchord/envd/commit/4cd6fef5d6284538ea523011aac47feec8dcf36a) chore(deps): bump github.com/moby/buildkit from 0.10.4 to 0.10.5 (#1081) - * [a81bbb0](https://github.com/tensorchord/envd/commit/a81bbb0aab9b1a7d1bf37a0374884f34af386c3b) chore(deps): bump github.com/urfave/cli/v2 from 2.20.2 to 2.20.3 (#1078) - * [a89f466](https://github.com/tensorchord/envd/commit/a89f466517aec054c0e0c5a295f09b5e7b3c33b7) chore(deps): bump github.com/opencontainers/image-spec from 1.1.0-rc1 to 1.1.0-rc2 (#1077) - * [a209f79](https://github.com/tensorchord/envd/commit/a209f79d07b945c2a1a87972f191be7f4428d6c1) chore(deps): bump github.com/onsi/gomega from 1.21.1 to 1.22.1 (#1080) - * [a8a6339](https://github.com/tensorchord/envd/commit/a8a633928a5ec70f5deccbebf81619bb1dbe33da) chore(deps): bump github.com/stretchr/testify from 1.8.0 to 1.8.1 (#1079) * [9faeebe](https://github.com/tensorchord/envd/commit/9faeebe27f47ce2ee1d99b04325eb41745390099) fix: Fix build.sh (#1072) * [ff30b3a](https://github.com/tensorchord/envd/commit/ff30b3a5a4b1ccdc089b601c1d4c2bfdd81ce61c) fix: Fix the file name typo (#1071) * [51b00fb](https://github.com/tensorchord/envd/commit/51b00fb189422b4791ef2c53cc80ac3a5067b67a) feat(context): Support unix context and daemonless (#1062) @@ -640,11 +1052,6 @@ * [cf78dd1](https://github.com/tensorchord/envd/commit/cf78dd131e869ad20fe67a4a1159d7bf69e6fb63) feat: Implement env client in envd engine (#1049) * [bc6255b](https://github.com/tensorchord/envd/commit/bc6255bc3d494be85afc7c132f2e48f72fffd0fa) example: Add torch profiler example (#1026) * [ded3fce](https://github.com/tensorchord/envd/commit/ded3fce1b481aab9c51c2f13dbb2d92288532da0) feat: add `envd down` as an alias for `envd destroy` (#1047) - * [9b8582d](https://github.com/tensorchord/envd/commit/9b8582d910e83288ef90ba45b599221341ad29f6) chore(deps): bump github.com/onsi/ginkgo/v2 from 2.2.0 to 2.3.1 (#1039) - * [0ee19ed](https://github.com/tensorchord/envd/commit/0ee19ed79d3332a25082878ca19c1bc032072aef) chore(deps): bump pypa/cibuildwheel from 2.10.2 to 2.11.1 (#1036) - * [6ba7633](https://github.com/tensorchord/envd/commit/6ba763368d81035b0f13e5eb1549856ecaaa30d1) chore(deps): bump k8s.io/api from 0.25.2 to 0.25.3 (#1038) - * [0500dca](https://github.com/tensorchord/envd/commit/0500dca9b5174510c1c741ec1d9cfbf97d09a2e9) chore(deps): bump github.com/urfave/cli/v2 from 2.19.2 to 2.20.2 (#1037) - * [847f187](https://github.com/tensorchord/envd/commit/847f187b45983a56093dd962d5638dae5f260743) chore(deps): bump github.com/onsi/gomega from 1.21.1 to 1.22.1 (#1040) * [1fb2721](https://github.com/tensorchord/envd/commit/1fb272197f405c5ee9dcca46c82079095afa406d) bug: Fix flaky auth test (#1034) * [d0134d3](https://github.com/tensorchord/envd/commit/d0134d301eba153dcda0c79a3a23babf75a14434) fix(zsh): ignore inserting zsh-completion if system don't have zsh shell (#1025) * [ec92a29](https://github.com/tensorchord/envd/commit/ec92a295b835664d559f201ba2542a6a609a560d) feat: support using current directory name as env name in envd describe (#1033) @@ -660,9 +1067,7 @@ * [46168da](https://github.com/tensorchord/envd/commit/46168dac70fb8cd9b841a32e35a414ad29ddfaa2) chore(test): Add test cases for pkg/home/auth.go (#1009) * [d87afda](https://github.com/tensorchord/envd/commit/d87afda9213fcb334ca55d247ba3c0663b7b180b) feat: Add envd-server runtime proposal (#303) * [fec7ada](https://github.com/tensorchord/envd/commit/fec7ada4efc2186fa5f858d5abe211a2beb2dfdd) fix: Remove hard code docker in envd engine init (#1000) - * [cf56f0d](https://github.com/tensorchord/envd/commit/cf56f0de7bfb05be47d4704c14f9bbb892a86d28) chore(deps): bump github.com/onsi/gomega from 1.20.2 to 1.21.1 (#997) * [a55470e](https://github.com/tensorchord/envd/commit/a55470e363b35b689649596b449b996ada701630) feat(build): detect if the current environment is running before building (#892) (#989) - * [d0219f2](https://github.com/tensorchord/envd/commit/d0219f2e3e999e27f334a39180031390a4554af9) chore(deps): bump github.com/urfave/cli/v2 from 2.17.1 to 2.19.2 (#998) * [b2a9018](https://github.com/tensorchord/envd/commit/b2a90188c012fa186de2ecb7b1aa534062681020) feat: Support cli argument for host key (#992) * [91ea50c](https://github.com/tensorchord/envd/commit/91ea50cd74e36f2d0d966d2b6a1ddea0ad15c43b) bug: fix pypi package information (#959) * [0be9969](https://github.com/tensorchord/envd/commit/0be9969471f8358f1cc283215ddf8e4cfcecebad) fix: Remove dump checkout and remove pre-commit (#982) @@ -671,8 +1076,6 @@ * [17fedb8](https://github.com/tensorchord/envd/commit/17fedb8d2ae41318e2f705687c334b7e42413ab3) fix(ir): make sure default value won't be replaced with empty value (#970) * [c1ae887](https://github.com/tensorchord/envd/commit/c1ae887f12b23802363ac417c198e19983c5605f) feat(CLI): Support runner in context (#961) * [bf993e2](https://github.com/tensorchord/envd/commit/bf993e2cab44502abb74a79e732b74a5567b6194) refact: conda/mamba create/update env, fix user permissions (#933) - * [0e79fb9](https://github.com/tensorchord/envd/commit/0e79fb91b9f3770f2e5356fc8a10182dd31e2bdf) chore(deps): bump github.com/urfave/cli/v2 from 2.16.3 to 2.17.1 (#968) - * [c9045d2](https://github.com/tensorchord/envd/commit/c9045d24d412099a1ac0bf9b9acf78135157351d) chore(deps): bump dependabot/fetch-metadata from 1.3.3 to 1.3.4 (#967) * [c22408c](https://github.com/tensorchord/envd/commit/c22408c8f3b87ef959bda7d7681203ffd8d6212c) fix(ir): `apt install` and `conda env create` cache (#962) * [006f653](https://github.com/tensorchord/envd/commit/006f6538396b47b1bcab2a0d53ec9dae0221c8f5) feat: envd-sshd can read public key path from environment variable (#954) * [0b73548](https://github.com/tensorchord/envd/commit/0b7354868943f9e4ea189be18eeea534959b5d6c) example: Use torch in mnist example (#927) @@ -680,14 +1083,10 @@ * [236cd0b](https://github.com/tensorchord/envd/commit/236cd0be8cdfa9e4731d3b69cec7027001c8770b) fix: version tag in build.sh (#947) * [2eef587](https://github.com/tensorchord/envd/commit/2eef587b44dfe77828570bac8a3e2a7def61c5e1) fix: r & julia sshd image (#945) * [b704029](https://github.com/tensorchord/envd/commit/b70402917edf874a0a8e630664b637fa8c22cd53) feat(ir): all in llb (#941) - * [f5f70e0](https://github.com/tensorchord/envd/commit/f5f70e0de304b8ff4767cd935ab3d307ed5599a2) chore(deps): bump pypa/cibuildwheel from 2.10.1 to 2.10.2 (#936) * [bd69c3d](https://github.com/tensorchord/envd/commit/bd69c3df326f1f213472960c099dff0c6d35e41c) feat: Support envd-server (#932) * [171b82f](https://github.com/tensorchord/envd/commit/171b82fcbac1da9543b9acda1911d110c322d802) fix: e2e doc test (#926) * [ce36545](https://github.com/tensorchord/envd/commit/ce36545d4865a6062ab4b6332a0e26e0d7030db2) feat(cli): rm image when destroying the env (#925) * [eecc7cf](https://github.com/tensorchord/envd/commit/eecc7cf48cf354ef3d72f17dfcf1d6f19bf90b85) feat: informative error message (#859) - * [a3fd464](https://github.com/tensorchord/envd/commit/a3fd4649f83f95d4e349348e4367bf564447780f) chore(deps): bump github.com/onsi/ginkgo/v2 from 2.1.6 to 2.2.0 (#919) - * [756c92d](https://github.com/tensorchord/envd/commit/756c92d48cb819149be5520948de122b687a25aa) chore(deps): bump github.com/urfave/cli/v2 from 2.16.2 to 2.16.3 (#918) - * [b5acc08](https://github.com/tensorchord/envd/commit/b5acc08d46b6b84a0c57d19a2e1b62e247785bd4) chore(deps): bump pypa/cibuildwheel from 2.9.0 to 2.10.1 (#917) ### Contributors @@ -702,7 +1101,6 @@ * Yijiang Liu * Yilong Li * Zhenzhen Zhao - * dependabot[bot] * nullday * wangxiaolei @@ -769,9 +1167,6 @@ * [171b82f](https://github.com/tensorchord/envd/commit/171b82fcbac1da9543b9acda1911d110c322d802) fix: e2e doc test (#926) * [ce36545](https://github.com/tensorchord/envd/commit/ce36545d4865a6062ab4b6332a0e26e0d7030db2) feat(cli): rm image when destroying the env (#925) * [eecc7cf](https://github.com/tensorchord/envd/commit/eecc7cf48cf354ef3d72f17dfcf1d6f19bf90b85) feat: informative error message (#859) - * [a3fd464](https://github.com/tensorchord/envd/commit/a3fd4649f83f95d4e349348e4367bf564447780f) chore(deps): bump github.com/onsi/ginkgo/v2 from 2.1.6 to 2.2.0 (#919) - * [756c92d](https://github.com/tensorchord/envd/commit/756c92d48cb819149be5520948de122b687a25aa) chore(deps): bump github.com/urfave/cli/v2 from 2.16.2 to 2.16.3 (#918) - * [b5acc08](https://github.com/tensorchord/envd/commit/b5acc08d46b6b84a0c57d19a2e1b62e247785bd4) chore(deps): bump pypa/cibuildwheel from 2.9.0 to 2.10.1 (#917) * [c3c0b4e](https://github.com/tensorchord/envd/commit/c3c0b4e33ab696c3863632d0d4179f8211813fcb) fix: Use macos 11 (#912) * [6721a88](https://github.com/tensorchord/envd/commit/6721a88aa1e2a97bfd3060e3168ab15b61f25609) chore(goreleaser): Skip homebrew (#909) @@ -780,7 +1175,6 @@ * Ce Gao * Jinjing Zhou * Keming - * dependabot[bot] ## v0.2.0-alpha.22 (2022-09-16) @@ -791,26 +1185,20 @@ * [2e8b5d5](https://github.com/tensorchord/envd/commit/2e8b5d5d4756b5c02c6d3e846b5be093fd6394b1) fix: include update repo (#885) * [eb2cdd1](https://github.com/tensorchord/envd/commit/eb2cdd1a65321a1530f59190cd40560e6c31d5a3) bug: Fix detach instruction message (#882) * [8a02b26](https://github.com/tensorchord/envd/commit/8a02b264ac16318c5d54660ca883414ef7a15cad) refact: add envd home path func (#880) - * [63daa5e](https://github.com/tensorchord/envd/commit/63daa5e870e5ea24c3f6e881e4932da2334688dc) chore(deps): bump github.com/spf13/viper from 1.12.0 to 1.13.0 (#875) - * [aa53bdb](https://github.com/tensorchord/envd/commit/aa53bdb478645863dae60ca37f1ebdbc7a564c56) chore(deps): bump github.com/urfave/cli/v2 from 2.14.0 to 2.16.2 (#874) ### Contributors * Ce Gao * Jinjing Zhou * Keming - * dependabot[bot] ## v0.2.0-alpha.21 (2022-09-12) - * [ddf3bdc](https://github.com/tensorchord/envd/commit/ddf3bdc8dd859683d7539d3c7f226b82cece40e7) chore(deps): bump actions/setup-go from 2 to 3 (#873) - * [7220958](https://github.com/tensorchord/envd/commit/72209583b941c4eb87b282004f7c8a1ae57410ae) chore(deps): bump actions/checkout from 2 to 3 (#872) * [f1b3fe5](https://github.com/tensorchord/envd/commit/f1b3fe5029091cffc0a1022b665d585132c5a8d8) chore(CLI): test new release for envd-sshd (#866) ### Contributors * Yuedong Wu - * dependabot[bot] ## v0.2.0-alpha.20 (2022-09-11) @@ -832,20 +1220,15 @@ * [ecb9e26](https://github.com/tensorchord/envd/commit/ecb9e2626e65b5e1f647d7385142c10677f2d7eb) refact: unify the path env (#855) * [8056fda](https://github.com/tensorchord/envd/commit/8056fda28febfe6cbe64502159b302b670970517) feat: add runtime graph to image label (#815) * [6ad1d4c](https://github.com/tensorchord/envd/commit/6ad1d4ca2085317850e9726910bc3024b743439e) refact: apt_source, io, config mode (#853) - * [07e2dc0](https://github.com/tensorchord/envd/commit/07e2dc0e6d4d7f3be2debf79389a792964e727b1) chore(deps): bump github.com/gliderlabs/ssh from 0.3.4 to 0.3.5 (#849) ### Contributors * Jinjing Zhou * Keming - * dependabot[bot] * nullday ## v0.2.0-alpha.18 (2022-09-06) - * [c0ba31a](https://github.com/tensorchord/envd/commit/c0ba31adc231d09600892f9445320df8eb84947b) chore(deps): bump github.com/onsi/gomega from 1.20.1 to 1.20.2 (#846) - * [13bc9a6](https://github.com/tensorchord/envd/commit/13bc9a61c4255215661c724fb7c54f2b81642a21) chore(deps): bump github.com/urfave/cli/v2 from 2.11.2 to 2.14.0 (#845) - * [11966f8](https://github.com/tensorchord/envd/commit/11966f8c0f997f57c45979d7857cac48ad1d1e5b) chore(deps): bump github.com/docker/go-units from 0.4.0 to 0.5.0 (#848) * [7ba3b3f](https://github.com/tensorchord/envd/commit/7ba3b3fa6b716efd3398a7cb3e533e915129717d) feat(cli): add msg when detach from container (#841) * [7fc9f34](https://github.com/tensorchord/envd/commit/7fc9f34d333b22013e847fd2c2312d18fb861068) fix: Update demo (#840) * [190ee76](https://github.com/tensorchord/envd/commit/190ee7635f0d9bbdee6c8b53d53a72dd8ca4e619) feat(lang): install.python_packages(local_wheels=[]) (#838) @@ -855,15 +1238,12 @@ * [404de31](https://github.com/tensorchord/envd/commit/404de3101cece0497084412433cf877f66cf5ee2) feat(lang): init py env by generating the bulid.envd (#827) * [36b1231](https://github.com/tensorchord/envd/commit/36b123142385d20fb7f7c1106c15c02f79ed4742) bug: fix permission issue when pip install from git repo (#829) * [1dcada4](https://github.com/tensorchord/envd/commit/1dcada4403d0c0bf8e916fc67b62c174f66df3d3) feat(build): Mount local build context into the run command (#822) - * [f70a11c](https://github.com/tensorchord/envd/commit/f70a11c5f251b8ed0f0f42cd422b4b93efabd4a7) chore(deps): bump github.com/onsi/gomega from 1.20.0 to 1.20.1 (#821) - * [3b440c6](https://github.com/tensorchord/envd/commit/3b440c60c59ee65444a92aa91e295be9be2125b0) chore(deps): bump github.com/moby/buildkit from 0.10.3 to 0.10.4 (#820) ### Contributors * Ce Gao * Jinjing Zhou * Keming - * dependabot[bot] ## v0.2.0-alpha.17 (2022-08-26) @@ -905,8 +1285,6 @@ * [2f82fa5](https://github.com/tensorchord/envd/commit/2f82fa5ab884671ba2a5058d38d205cbd20bce1f) proposal: daemon process (#769) * [d06b878](https://github.com/tensorchord/envd/commit/d06b8786e2963f4bf5cc92b90fdcc80621a4bd5e) doc: update python api doc (#759) * [ce2e8b2](https://github.com/tensorchord/envd/commit/ce2e8b2b4e755b52892d9b3597489daae19f1dad) fix: Remove empty token arg (#772) - * [8ab89b9](https://github.com/tensorchord/envd/commit/8ab89b967caa104ad839a83d7d9ece186eb80918) chore(deps): bump github.com/urfave/cli/v2 from 2.11.1 to 2.11.2 (#775) - * [3a6b127](https://github.com/tensorchord/envd/commit/3a6b12772b956eb31b6ccc6c31e62efbc3feb6e3) chore(deps): bump pypa/cibuildwheel from 2.8.1 to 2.9.0 (#774) * [ec8cae1](https://github.com/tensorchord/envd/commit/ec8cae17b26de61bbb73026540db56505cffb2a8) fix: remove unnecessary if statement (#773) ### Contributors @@ -915,7 +1293,6 @@ * Keming * Wei Zhang * Zhizhen He - * dependabot[bot] ## v0.2.0-alpha.14 (2022-08-12) @@ -1010,11 +1387,6 @@ * [b3ee633](https://github.com/tensorchord/envd/commit/b3ee6334a54e7d89b3f2631631c112c1d5ecac6a) fix: Fix lint issues in conda env yaml feature PR (#679) * [a796a12](https://github.com/tensorchord/envd/commit/a796a129d83e5c707bb9d0c1c32295208b136495) feat(lang): Support env.yaml in conda_packages (#674) * [af3e78d](https://github.com/tensorchord/envd/commit/af3e78d6733f53f5d31eda045725c0af599aefa6) feature: add label ai.tensorchord.envd.build.manifestBytecodeHash to image for cache robust (#661) - * [3044002](https://github.com/tensorchord/envd/commit/30440022266b908adbb790e59c6696b36bf92b28) chore(deps): bump github.com/onsi/gomega from 1.19.0 to 1.20.0 (#657) - * [400fd7f](https://github.com/tensorchord/envd/commit/400fd7f8af0a04d1d0aa5fd7f047fea59172a7ef) chore(deps): bump github.com/urfave/cli/v2 from 2.11.0 to 2.11.1 (#655) - * [30e8caf](https://github.com/tensorchord/envd/commit/30e8cafc80b04b1bfa6e4d9d2ba273928c8b3919) chore(deps): bump actions/upload-artifact from 2 to 3 (#654) - * [04360d6](https://github.com/tensorchord/envd/commit/04360d63ba984a1a58faaf300d0cee64269d442a) chore(deps): bump pypa/cibuildwheel from 2.8.0 to 2.8.1 (#653) - * [de6c59e](https://github.com/tensorchord/envd/commit/de6c59e730da4520bccc747bb941bb12ced913d7) chore(deps): bump github.com/sirupsen/logrus from 1.8.1 to 1.9.0 (#656) * [62091a4](https://github.com/tensorchord/envd/commit/62091a460b5502d24fa57a04ad19cf273c832832) fix(build): Fix image config (#651) * [4c1df28](https://github.com/tensorchord/envd/commit/4c1df286df7c3d5c4a99b4bbc5958f86d08e0738) fix: context create with 'use' (#652) * [6f05072](https://github.com/tensorchord/envd/commit/6f05072f1f2a72b55d8e2bac468707219ceb30b4) feat(CLI): Support cache (#648) @@ -1024,7 +1396,6 @@ * Ce Gao * Keming - * dependabot[bot] * nullday * wyq @@ -1034,13 +1405,11 @@ * [890119d](https://github.com/tensorchord/envd/commit/890119d119e7becf9f39f392aa053ae1e70c77c0) fix: check manifest and image update in new gateway buildfunc (#624) * [c8471db](https://github.com/tensorchord/envd/commit/c8471db20f0a7e48c2c8346b3ab88637445200f5) support buildkit TCP socket (#599) * [ef8a90d](https://github.com/tensorchord/envd/commit/ef8a90df3bc1a3009381eb3fd10767f468fcefc2) feat: Refactor with Builder.Options (#615) - * [2a88ad1](https://github.com/tensorchord/envd/commit/2a88ad120b2a24b5095883a1e18de55189ff643f) chore(deps): bump github.com/urfave/cli/v2 from 2.10.3 to 2.11.0 (#610) ### Contributors * Ce Gao * Keming - * dependabot[bot] * nullday ## v0.2.0-alpha.6 (2022-07-15) @@ -1054,14 +1423,12 @@ * [fa041a8](https://github.com/tensorchord/envd/commit/fa041a849dde7f61f82fa2afdf105e45efc888ef) fix: Pre-mkdir the .cache directory of user envd (#592) * [54dfc52](https://github.com/tensorchord/envd/commit/54dfc52f4adc5cbce5de806dc6186e39adf21e0d) bug: fix missing function in example mnist (#589) * [00249bb](https://github.com/tensorchord/envd/commit/00249bbd2330088dc584015353837917bd557503) Use DefaultText in up.go (#587) - * [302e449](https://github.com/tensorchord/envd/commit/302e4490992d5f74bf5a9e08293cfb42a89e0367) chore(deps): bump pypa/cibuildwheel from 2.7.0 to 2.8.0 (#583) ### Contributors * Ce Gao * Guangyang Li * Jinjing Zhou - * dependabot[bot] * nullday ## v0.2.0-alpha.5 (2022-07-08) @@ -1087,14 +1454,11 @@ * [6e9e44d](https://github.com/tensorchord/envd/commit/6e9e44dfadf13c3899707beda09d92b4f907e24d) feat: Support specify build target (#497) * [e443784](https://github.com/tensorchord/envd/commit/e44378470ddd029e3f2c94c93e00b0399e89b772) feat(lang): Support RStudio server (#503) - * [89eb6e8](https://github.com/tensorchord/envd/commit/89eb6e8b5bdf795f2f1b145b75b6d87745284d71) chore(deps): bump github.com/stretchr/testify from 1.7.5 to 1.8.0 (#540) - * [74b27e9](https://github.com/tensorchord/envd/commit/74b27e9d0039c2558b3ebd152260f0c163fc7d37) chore(deps): bump dependabot/fetch-metadata from 1.3.1 to 1.3.3 (#539) ### Contributors * Ce Gao * Jinjing Zhou - * dependabot[bot] ## v0.2.0-alpha.3 (2022-07-01) @@ -1140,10 +1504,6 @@ * [2e8b5d5](https://github.com/tensorchord/envd/commit/2e8b5d5d4756b5c02c6d3e846b5be093fd6394b1) fix: include update repo (#885) * [eb2cdd1](https://github.com/tensorchord/envd/commit/eb2cdd1a65321a1530f59190cd40560e6c31d5a3) bug: Fix detach instruction message (#882) * [8a02b26](https://github.com/tensorchord/envd/commit/8a02b264ac16318c5d54660ca883414ef7a15cad) refact: add envd home path func (#880) - * [63daa5e](https://github.com/tensorchord/envd/commit/63daa5e870e5ea24c3f6e881e4932da2334688dc) chore(deps): bump github.com/spf13/viper from 1.12.0 to 1.13.0 (#875) - * [aa53bdb](https://github.com/tensorchord/envd/commit/aa53bdb478645863dae60ca37f1ebdbc7a564c56) chore(deps): bump github.com/urfave/cli/v2 from 2.14.0 to 2.16.2 (#874) - * [ddf3bdc](https://github.com/tensorchord/envd/commit/ddf3bdc8dd859683d7539d3c7f226b82cece40e7) chore(deps): bump actions/setup-go from 2 to 3 (#873) - * [7220958](https://github.com/tensorchord/envd/commit/72209583b941c4eb87b282004f7c8a1ae57410ae) chore(deps): bump actions/checkout from 2 to 3 (#872) * [f1b3fe5](https://github.com/tensorchord/envd/commit/f1b3fe5029091cffc0a1022b665d585132c5a8d8) chore(CLI): test new release for envd-sshd (#866) * [49d79fb](https://github.com/tensorchord/envd/commit/49d79fb17bee4a2baaeadd500607cba7d8426b28) fix: Update readme (#865) * [d7995a7](https://github.com/tensorchord/envd/commit/d7995a7171cbe48a65aad3e3b56077ffee9a625a) feat(lang): io.http download files to extra_source (#858) @@ -1153,10 +1513,6 @@ * [ecb9e26](https://github.com/tensorchord/envd/commit/ecb9e2626e65b5e1f647d7385142c10677f2d7eb) refact: unify the path env (#855) * [8056fda](https://github.com/tensorchord/envd/commit/8056fda28febfe6cbe64502159b302b670970517) feat: add runtime graph to image label (#815) * [6ad1d4c](https://github.com/tensorchord/envd/commit/6ad1d4ca2085317850e9726910bc3024b743439e) refact: apt_source, io, config mode (#853) - * [07e2dc0](https://github.com/tensorchord/envd/commit/07e2dc0e6d4d7f3be2debf79389a792964e727b1) chore(deps): bump github.com/gliderlabs/ssh from 0.3.4 to 0.3.5 (#849) - * [c0ba31a](https://github.com/tensorchord/envd/commit/c0ba31adc231d09600892f9445320df8eb84947b) chore(deps): bump github.com/onsi/gomega from 1.20.1 to 1.20.2 (#846) - * [13bc9a6](https://github.com/tensorchord/envd/commit/13bc9a61c4255215661c724fb7c54f2b81642a21) chore(deps): bump github.com/urfave/cli/v2 from 2.11.2 to 2.14.0 (#845) - * [11966f8](https://github.com/tensorchord/envd/commit/11966f8c0f997f57c45979d7857cac48ad1d1e5b) chore(deps): bump github.com/docker/go-units from 0.4.0 to 0.5.0 (#848) * [7ba3b3f](https://github.com/tensorchord/envd/commit/7ba3b3fa6b716efd3398a7cb3e533e915129717d) feat(cli): add msg when detach from container (#841) * [7fc9f34](https://github.com/tensorchord/envd/commit/7fc9f34d333b22013e847fd2c2312d18fb861068) fix: Update demo (#840) * [190ee76](https://github.com/tensorchord/envd/commit/190ee7635f0d9bbdee6c8b53d53a72dd8ca4e619) feat(lang): install.python_packages(local_wheels=[]) (#838) @@ -1166,8 +1522,6 @@ * [404de31](https://github.com/tensorchord/envd/commit/404de3101cece0497084412433cf877f66cf5ee2) feat(lang): init py env by generating the bulid.envd (#827) * [36b1231](https://github.com/tensorchord/envd/commit/36b123142385d20fb7f7c1106c15c02f79ed4742) bug: fix permission issue when pip install from git repo (#829) * [1dcada4](https://github.com/tensorchord/envd/commit/1dcada4403d0c0bf8e916fc67b62c174f66df3d3) feat(build): Mount local build context into the run command (#822) - * [f70a11c](https://github.com/tensorchord/envd/commit/f70a11c5f251b8ed0f0f42cd422b4b93efabd4a7) chore(deps): bump github.com/onsi/gomega from 1.20.0 to 1.20.1 (#821) - * [3b440c6](https://github.com/tensorchord/envd/commit/3b440c60c59ee65444a92aa91e295be9be2125b0) chore(deps): bump github.com/moby/buildkit from 0.10.3 to 0.10.4 (#820) * [82fbc87](https://github.com/tensorchord/envd/commit/82fbc87ade5637fe7db8e2c0087a1555206dc1b1) doc: add include, refine others (#817) * [630ada1](https://github.com/tensorchord/envd/commit/630ada172bdf876c3b749329fdbe284c108051f2) feat(lang): support include other git repo for envd functions/variables (#808) * [5c4971b](https://github.com/tensorchord/envd/commit/5c4971b2f5fa6b32c2c247e18cf6c6a178d28f57) feat(CLI): envd env describe expose info (#801) @@ -1187,8 +1541,6 @@ * [2f82fa5](https://github.com/tensorchord/envd/commit/2f82fa5ab884671ba2a5058d38d205cbd20bce1f) proposal: daemon process (#769) * [d06b878](https://github.com/tensorchord/envd/commit/d06b8786e2963f4bf5cc92b90fdcc80621a4bd5e) doc: update python api doc (#759) * [ce2e8b2](https://github.com/tensorchord/envd/commit/ce2e8b2b4e755b52892d9b3597489daae19f1dad) fix: Remove empty token arg (#772) - * [8ab89b9](https://github.com/tensorchord/envd/commit/8ab89b967caa104ad839a83d7d9ece186eb80918) chore(deps): bump github.com/urfave/cli/v2 from 2.11.1 to 2.11.2 (#775) - * [3a6b127](https://github.com/tensorchord/envd/commit/3a6b12772b956eb31b6ccc6c31e62efbc3feb6e3) chore(deps): bump pypa/cibuildwheel from 2.8.1 to 2.9.0 (#774) * [ec8cae1](https://github.com/tensorchord/envd/commit/ec8cae17b26de61bbb73026540db56505cffb2a8) fix: remove unnecessary if statement (#773) * [b9f0af8](https://github.com/tensorchord/envd/commit/b9f0af8056129fe6c5a2e590cd428463186cac0e) fix: -path and -file bug (#766) * [4a359f1](https://github.com/tensorchord/envd/commit/4a359f1eb608a0680a94379b4ac52756d605be9d) fix(release): :hammer: drop go build dep for homebrew (#768) @@ -1232,11 +1584,6 @@ * [b3ee633](https://github.com/tensorchord/envd/commit/b3ee6334a54e7d89b3f2631631c112c1d5ecac6a) fix: Fix lint issues in conda env yaml feature PR (#679) * [a796a12](https://github.com/tensorchord/envd/commit/a796a129d83e5c707bb9d0c1c32295208b136495) feat(lang): Support env.yaml in conda_packages (#674) * [af3e78d](https://github.com/tensorchord/envd/commit/af3e78d6733f53f5d31eda045725c0af599aefa6) feature: add label ai.tensorchord.envd.build.manifestBytecodeHash to image for cache robust (#661) - * [3044002](https://github.com/tensorchord/envd/commit/30440022266b908adbb790e59c6696b36bf92b28) chore(deps): bump github.com/onsi/gomega from 1.19.0 to 1.20.0 (#657) - * [400fd7f](https://github.com/tensorchord/envd/commit/400fd7f8af0a04d1d0aa5fd7f047fea59172a7ef) chore(deps): bump github.com/urfave/cli/v2 from 2.11.0 to 2.11.1 (#655) - * [30e8caf](https://github.com/tensorchord/envd/commit/30e8cafc80b04b1bfa6e4d9d2ba273928c8b3919) chore(deps): bump actions/upload-artifact from 2 to 3 (#654) - * [04360d6](https://github.com/tensorchord/envd/commit/04360d63ba984a1a58faaf300d0cee64269d442a) chore(deps): bump pypa/cibuildwheel from 2.8.0 to 2.8.1 (#653) - * [de6c59e](https://github.com/tensorchord/envd/commit/de6c59e730da4520bccc747bb941bb12ced913d7) chore(deps): bump github.com/sirupsen/logrus from 1.8.1 to 1.9.0 (#656) * [62091a4](https://github.com/tensorchord/envd/commit/62091a460b5502d24fa57a04ad19cf273c832832) fix(build): Fix image config (#651) * [4c1df28](https://github.com/tensorchord/envd/commit/4c1df286df7c3d5c4a99b4bbc5958f86d08e0738) fix: context create with 'use' (#652) * [6f05072](https://github.com/tensorchord/envd/commit/6f05072f1f2a72b55d8e2bac468707219ceb30b4) feat(CLI): Support cache (#648) @@ -1245,7 +1592,6 @@ * [890119d](https://github.com/tensorchord/envd/commit/890119d119e7becf9f39f392aa053ae1e70c77c0) fix: check manifest and image update in new gateway buildfunc (#624) * [c8471db](https://github.com/tensorchord/envd/commit/c8471db20f0a7e48c2c8346b3ab88637445200f5) support buildkit TCP socket (#599) * [ef8a90d](https://github.com/tensorchord/envd/commit/ef8a90df3bc1a3009381eb3fd10767f468fcefc2) feat: Refactor with Builder.Options (#615) - * [2a88ad1](https://github.com/tensorchord/envd/commit/2a88ad120b2a24b5095883a1e18de55189ff643f) chore(deps): bump github.com/urfave/cli/v2 from 2.10.3 to 2.11.0 (#610) * [18abe90](https://github.com/tensorchord/envd/commit/18abe90072835534f75b392b5f1ef6dbbf0bbeb5) feat(builder): Abstract BuildFunc to use gateway client (#606) * [178b8da](https://github.com/tensorchord/envd/commit/178b8dafdd4357688a911bfff103c397b08410a9) feat(WSL): Add ssh config entry to Windows ssh config if using WSL (#604) * [ceb07f5](https://github.com/tensorchord/envd/commit/ceb07f5fc43ced6da9ada711eb8c127d14afa969) fix: set conda as the only python provider (#602) @@ -1255,7 +1601,6 @@ * [fa041a8](https://github.com/tensorchord/envd/commit/fa041a849dde7f61f82fa2afdf105e45efc888ef) fix: Pre-mkdir the .cache directory of user envd (#592) * [54dfc52](https://github.com/tensorchord/envd/commit/54dfc52f4adc5cbce5de806dc6186e39adf21e0d) bug: fix missing function in example mnist (#589) * [00249bb](https://github.com/tensorchord/envd/commit/00249bbd2330088dc584015353837917bd557503) Use DefaultText in up.go (#587) - * [302e449](https://github.com/tensorchord/envd/commit/302e4490992d5f74bf5a9e08293cfb42a89e0367) chore(deps): bump pypa/cibuildwheel from 2.7.0 to 2.8.0 (#583) * [6cfc0f1](https://github.com/tensorchord/envd/commit/6cfc0f16224095605dd85fb38b9cf406fbb65118) feat: Support for build image update when exec build or up again (#570) * [8f89e4b](https://github.com/tensorchord/envd/commit/8f89e4be3d154728824c67f13086eb727f545400) Fix: image tag normalized to docker spec (#573) * [3fe3757](https://github.com/tensorchord/envd/commit/3fe375769487a4eaf224a6db1437c840002c7a15) fix: add -c for every single conda channel (#569) @@ -1266,8 +1611,6 @@ * [f71cd7f](https://github.com/tensorchord/envd/commit/f71cd7f4891fab5e1be588e9b991ab4942e02d57) feat(CLI): Unify CLI style about env and image (#550) * [6e9e44d](https://github.com/tensorchord/envd/commit/6e9e44dfadf13c3899707beda09d92b4f907e24d) feat: Support specify build target (#497) * [e443784](https://github.com/tensorchord/envd/commit/e44378470ddd029e3f2c94c93e00b0399e89b772) feat(lang): Support RStudio server (#503) - * [89eb6e8](https://github.com/tensorchord/envd/commit/89eb6e8b5bdf795f2f1b145b75b6d87745284d71) chore(deps): bump github.com/stretchr/testify from 1.7.5 to 1.8.0 (#540) - * [74b27e9](https://github.com/tensorchord/envd/commit/74b27e9d0039c2558b3ebd152260f0c163fc7d37) chore(deps): bump dependabot/fetch-metadata from 1.3.1 to 1.3.3 (#539) * [dbac24d](https://github.com/tensorchord/envd/commit/dbac24d7931832d0fed12e931e544d31a557626d) feat(docker): Add entrypoint and ports in image config (#533) * [60f85f5](https://github.com/tensorchord/envd/commit/60f85f53c62597bc47f9d4328bb071b9795e0474) fix(README): Update coverage (#536) * [8276d7d](https://github.com/tensorchord/envd/commit/8276d7da92658a2d885a00c5039a87fb78cb2376) feat(CLI): Add push (#531) @@ -1278,8 +1621,6 @@ * [523fb40](https://github.com/tensorchord/envd/commit/523fb400742ed5b539f44232d9eedad8eaefd13c) feat(CLI): Support context (#512) * [fde448a](https://github.com/tensorchord/envd/commit/fde448aff613306cb5ff3d763c06f05de12ec338) feat: add envd init (#514) * [8722564](https://github.com/tensorchord/envd/commit/8722564b12f0c96c58fca5d912c7c9c2e57c77a6) enhancement(CLI): Use upper case in CLI description (#515) - * [0ae8df9](https://github.com/tensorchord/envd/commit/0ae8df9e9eb4ec0c755920a7a3697cbab12b22e3) chore(deps): bump github.com/stretchr/testify from 1.7.2 to 1.7.5 (#505) - * [1cd5a27](https://github.com/tensorchord/envd/commit/1cd5a27027dd696276fa75e1d52a02858b7e7de8) chore(deps): bump github.com/urfave/cli/v2 from 2.8.1 to 2.10.3 (#504) * [ca6435d](https://github.com/tensorchord/envd/commit/ca6435ddfd29f5be2e85a1039da50c4db2fea03f) feat(lang): Support julia (#495) * [220d874](https://github.com/tensorchord/envd/commit/220d874712d7f7120f16337324d23b440163e3f7) feat(ssh): Config ssh key permanently and globally (#487) * [7127365](https://github.com/tensorchord/envd/commit/7127365e47c6a2923ce93ecf0164df49810b13fc) feat(lang): Support R language (#491) @@ -1315,7 +1656,6 @@ * Zhenguo.Li * Zhenzhen Zhao * Zhizhen He - * dependabot[bot] * kenwoodjw * nullday * wyq @@ -1337,10 +1677,6 @@ * [6e1cf05](https://github.com/tensorchord/envd/commit/6e1cf0509844060040bbd85d4f19e29410fb7a6f) fix: replace useless .editorconfig (#440) * [1c23cea](https://github.com/tensorchord/envd/commit/1c23cea84bfb37f2cd5ee0df63bf625448147994) release: Separate alpha and stable release in Homebrew (#439) * [274e183](https://github.com/tensorchord/envd/commit/274e18317597bb7a7a6413f59a52e7d5274ac85c) Update PyTorch installation CMD in examples (#435) - * [cfda1be](https://github.com/tensorchord/envd/commit/cfda1bed6f9861f198d0a37ebc8f46ed4bbb51ab) chore(deps): bump pypa/cibuildwheel from 2.6.1 to 2.7.0 (#428) - * [d25157e](https://github.com/tensorchord/envd/commit/d25157ef8169f2aa7c7169dfa18b389041a036a0) chore(deps): bump github.com/spf13/viper from 1.4.0 to 1.12.0 (#430) - * [d394410](https://github.com/tensorchord/envd/commit/d394410331eb25d20a33b6adef68506cbc0b6602) chore(deps): bump goreleaser/goreleaser-action from 2 to 3 (#427) - * [7070506](https://github.com/tensorchord/envd/commit/7070506f6c2acb44692dd06090ec9d614927ef65) chore(deps): bump github.com/gliderlabs/ssh from 0.3.3 to 0.3.4 (#429) * [2e287c9](https://github.com/tensorchord/envd/commit/2e287c9b1ea4d16c822532e56a39ae67bdf1982c) chore(destroy): add current path `.` as the default path (#422) * [750db5a](https://github.com/tensorchord/envd/commit/750db5a20328f5cb10a118ae348b5545dfef5a1f) chore(Makefile): add `help` target (#421) * [0c00005](https://github.com/tensorchord/envd/commit/0c0000571b7ffb1a4a9af418b1c91a1c28253ce8) Bootstrap gets error if the envd_buildkitd was stopped before (#417) @@ -1353,7 +1689,6 @@ * Kevin Su * Yuchen Cheng * Zhenzhen Zhao - * dependabot[bot] * kenwoodjw ## v0.1.0-alpha.12 (2022-06-17) @@ -1428,10 +1763,6 @@ * [85123b6](https://github.com/tensorchord/envd/commit/85123b6d427363f595f22ae0410bec6de7a092ef) fix: fix typo (#324) * [559e143](https://github.com/tensorchord/envd/commit/559e1435a9585fa32cee3eff73a11a577bcec111) chore(CI): Enable code coverage (#323) * [a4cb9dc](https://github.com/tensorchord/envd/commit/a4cb9dc0bf93970d604ac94dd88074f641bcff9b) fix(release): Change docker user (#321) - * [a5c3427](https://github.com/tensorchord/envd/commit/a5c3427c3feb38dbe5f0d7fdd633a8c978129837) chore(deps): bump github.com/moby/buildkit from 0.10.1 to 0.10.3 (#313) - * [43ad124](https://github.com/tensorchord/envd/commit/43ad1249ff938277b65bb91d7d8cf6a128380ad3) chore(deps): bump github.com/pkg/sftp from 1.13.4 to 1.13.5 (#309) - * [5a9c947](https://github.com/tensorchord/envd/commit/5a9c947edf1763745f1523ba7e4f2acf5f476990) chore(deps): bump github.com/stretchr/testify from 1.7.0 to 1.7.2 (#310) - * [4fb34e2](https://github.com/tensorchord/envd/commit/4fb34e29471b0a5625d075af02854f784aceb8a7) chore(deps): bump github.com/urfave/cli/v2 from 2.4.0 to 2.8.1 (#312) * [0c63064](https://github.com/tensorchord/envd/commit/0c6306476922f0abcf30771a7d724b150e2188b6) fix: add api/__init__.py (#317) ### Contributors @@ -1444,7 +1775,6 @@ * Yuan Tang * Yuchen Cheng * Zhizhen He - * dependabot[bot] * kenwoodjw ## v0.1.0-alpha.6 (2022-06-13) @@ -1516,10 +1846,6 @@ * [6e1cf05](https://github.com/tensorchord/envd/commit/6e1cf0509844060040bbd85d4f19e29410fb7a6f) fix: replace useless .editorconfig (#440) * [1c23cea](https://github.com/tensorchord/envd/commit/1c23cea84bfb37f2cd5ee0df63bf625448147994) release: Separate alpha and stable release in Homebrew (#439) * [274e183](https://github.com/tensorchord/envd/commit/274e18317597bb7a7a6413f59a52e7d5274ac85c) Update PyTorch installation CMD in examples (#435) - * [cfda1be](https://github.com/tensorchord/envd/commit/cfda1bed6f9861f198d0a37ebc8f46ed4bbb51ab) chore(deps): bump pypa/cibuildwheel from 2.6.1 to 2.7.0 (#428) - * [d25157e](https://github.com/tensorchord/envd/commit/d25157ef8169f2aa7c7169dfa18b389041a036a0) chore(deps): bump github.com/spf13/viper from 1.4.0 to 1.12.0 (#430) - * [d394410](https://github.com/tensorchord/envd/commit/d394410331eb25d20a33b6adef68506cbc0b6602) chore(deps): bump goreleaser/goreleaser-action from 2 to 3 (#427) - * [7070506](https://github.com/tensorchord/envd/commit/7070506f6c2acb44692dd06090ec9d614927ef65) chore(deps): bump github.com/gliderlabs/ssh from 0.3.3 to 0.3.4 (#429) * [2e287c9](https://github.com/tensorchord/envd/commit/2e287c9b1ea4d16c822532e56a39ae67bdf1982c) chore(destroy): add current path `.` as the default path (#422) * [750db5a](https://github.com/tensorchord/envd/commit/750db5a20328f5cb10a118ae348b5545dfef5a1f) chore(Makefile): add `help` target (#421) * [0c00005](https://github.com/tensorchord/envd/commit/0c0000571b7ffb1a4a9af418b1c91a1c28253ce8) Bootstrap gets error if the envd_buildkitd was stopped before (#417) @@ -1552,10 +1878,6 @@ * [85123b6](https://github.com/tensorchord/envd/commit/85123b6d427363f595f22ae0410bec6de7a092ef) fix: fix typo (#324) * [559e143](https://github.com/tensorchord/envd/commit/559e1435a9585fa32cee3eff73a11a577bcec111) chore(CI): Enable code coverage (#323) * [a4cb9dc](https://github.com/tensorchord/envd/commit/a4cb9dc0bf93970d604ac94dd88074f641bcff9b) fix(release): Change docker user (#321) - * [a5c3427](https://github.com/tensorchord/envd/commit/a5c3427c3feb38dbe5f0d7fdd633a8c978129837) chore(deps): bump github.com/moby/buildkit from 0.10.1 to 0.10.3 (#313) - * [43ad124](https://github.com/tensorchord/envd/commit/43ad1249ff938277b65bb91d7d8cf6a128380ad3) chore(deps): bump github.com/pkg/sftp from 1.13.4 to 1.13.5 (#309) - * [5a9c947](https://github.com/tensorchord/envd/commit/5a9c947edf1763745f1523ba7e4f2acf5f476990) chore(deps): bump github.com/stretchr/testify from 1.7.0 to 1.7.2 (#310) - * [4fb34e2](https://github.com/tensorchord/envd/commit/4fb34e29471b0a5625d075af02854f784aceb8a7) chore(deps): bump github.com/urfave/cli/v2 from 2.4.0 to 2.8.1 (#312) * [0c63064](https://github.com/tensorchord/envd/commit/0c6306476922f0abcf30771a7d724b150e2188b6) fix: add api/__init__.py (#317) * [12cf334](https://github.com/tensorchord/envd/commit/12cf3345b6c09106508271dd84bd41bc03ceedbc) fix: Fix twine (#301) * [f42e162](https://github.com/tensorchord/envd/commit/f42e1625fd11332411b821931d0664494bfc1927) fix: Instal twine (#300) @@ -1609,7 +1931,6 @@ * Yuchen Cheng * Zhenzhen Zhao * Zhizhen He - * dependabot[bot] * kenwoodjw ## v0.0.1-rc.1 (2022-06-02) diff --git a/MANIFEST.in b/MANIFEST.in index 91bab2ab7..2142d25c8 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,9 +2,14 @@ include LICENSE include README.md include Makefile include .goreleaser.yaml .goreleaser/*.Dockerfile -include pkg/* cmd/* include go.mod go.sum include .GIT_TAG_INFO +graft pkg +graft cmd prune examples +prune bin +prune hack +prune e2e +prune dist prune docs prune .github diff --git a/Makefile b/Makefile index 93bfad056..ede0a1d69 100644 --- a/Makefile +++ b/Makefile @@ -83,6 +83,7 @@ GOPATH ?= $(shell go env GOPATH) GOROOT ?= $(shell go env GOROOT) BIN_DIR := $(GOPATH)/bin GOLANGCI_LINT := $(BIN_DIR)/golangci-lint +MOCKGEN := $(BIN_DIR)/mockgen # Default golang flags used in build and test # -mod=vendor: force go to use the vendor files instead of using the `$GOPATH/pkg/mod` @@ -101,7 +102,7 @@ export GOFLAGS ?= -count=1 build-release: @for target in $(TARGETS); do \ - CGO_ENABLED=$(CGO_ENABLED) go build -trimpath -v -o $(OUTPUT_DIR)/$${target} \ + CGO_ENABLED=$(CGO_ENABLED) go build -trimpath -o $(OUTPUT_DIR)/$${target} \ -ldflags "-s -w -X $(ROOT)/pkg/version.version=$(VERSION) \ -X $(ROOT)/pkg/version.buildDate=$(BUILD_DATE) \ -X $(ROOT)/pkg/version.gitCommit=$(GIT_COMMIT) \ @@ -133,16 +134,18 @@ addlicense-install: go install github.com/google/addlicense@latest build-local: - @for target in $(TARGETS); do \ - CGO_ENABLED=$(CGO_ENABLED) go build -trimpath -v -o $(OUTPUT_DIR)/$${target} \ + @for target in $(TARGETS); do \ + echo "Building $${target} ..."; \ + CGO_ENABLED=$(CGO_ENABLED) go build -trimpath -o $(OUTPUT_DIR)/$${target} \ -ldflags "-s -w -X $(ROOT)/pkg/version.version=$(VERSION) \ -X $(ROOT)/pkg/version.buildDate=$(BUILD_DATE) \ -X $(ROOT)/pkg/version.gitCommit=$(GIT_COMMIT) \ - -X $(ROOT)/pkg/version.gitTreeState=$(GIT_TREE_STATE) \ + -X $(ROOT)/pkg/version.gitTreeState=$(GIT_TREE_STATE) \ -X $(ROOT)/pkg/version.gitTag=$(GIT_LATEST_TAG) \ -X $(ROOT)/pkg/version.developmentFlag=true" \ - $(CMD_DIR)/$${target}; \ + $(CMD_DIR)/$${target}; \ done + @echo "Build envd successfully!" pypi-build: clean @python3 setup.py sdist bdist_wheel @@ -152,15 +155,16 @@ dev: clean build-local ## install envd command for local debug @pip3 install --force-reinstall dist/*.whl generate: mockgen-install ## Generate mocks - @mockgen -source pkg/buildkitd/buildkitd.go -destination pkg/buildkitd/mock/mock.go -package mock - @mockgen -source pkg/lang/frontend/starlark/interpreter.go -destination pkg/lang/frontend/starlark/mock/mock.go -package mock - @mockgen -source pkg/progress/compileui/display.go -destination pkg/progress/compileui/mock/mock.go -package mock + @$(MOCKGEN) -source pkg/buildkitd/buildkitd.go -destination pkg/buildkitd/mock/mock.go -package mock + @$(MOCKGEN) -source pkg/lang/frontend/starlark/interpreter.go -destination pkg/lang/frontend/starlark/mock/mock.go -package mock + @$(MOCKGEN) -source pkg/progress/compileui/display.go -destination pkg/progress/compileui/mock/mock.go -package mock # It is used by vscode to attach into the process. debug-local: @for target in $(TARGETS); do \ CGO_ENABLED=$(CGO_ENABLED) go build \ -v -o $(DEBUG_DIR)/$${target} \ + -ldflags="-X $(ROOT)/pkg/version.gitTag=$(GIT_LATEST_TAG)" \ -gcflags='all=-N -l' \ $(CMD_DIR)/$${target}; \ done @@ -172,7 +176,7 @@ test-local: @go test -v -race -coverprofile=coverage.out ./... test: generate ## Run the tests - @go test -race -coverpkg=./pkg/... -coverprofile=coverage.out $(shell go list ./... | grep -v e2e) + @go test -race -coverpkg=./pkg/... -coverprofile=coverage.out ./pkg/... @go tool cover -func coverage.out | tail -n 1 | awk '{ print "Total coverage: " $$3 }' e2e-test: @@ -184,7 +188,6 @@ e2e-test: -X $(ROOT)/pkg/version.developmentFlag=true" \ -race -v -timeout 20m -coverpkg=./pkg/... -coverprofile=e2e-coverage.out ./e2e - e2e-cli-test: @go test -ldflags "-s -w -X $(ROOT)/pkg/version.version=$(VERSION) \ -X $(ROOT)/pkg/version.buildDate=$(BUILD_DATE) \ @@ -192,7 +195,7 @@ e2e-cli-test: -X $(ROOT)/pkg/version.gitTreeState=$(GIT_TREE_STATE) \ -X $(ROOT)/pkg/version.gitTag="$(shell git describe --tags --abbrev=0)" \ -X $(ROOT)/pkg/version.developmentFlag=true" \ - -race -v -timeout 20m -coverpkg=./pkg/... -coverprofile=e2e-cli-coverage.out ./e2e/v0/cli + -race -v -timeout 20m -coverpkg=./pkg/... -coverprofile=e2e-cli-coverage.out ./e2e/cli e2e-lang-test: @go test -ldflags "-s -w -X $(ROOT)/pkg/version.version=$(VERSION) \ @@ -201,7 +204,7 @@ e2e-lang-test: -X $(ROOT)/pkg/version.gitTreeState=$(GIT_TREE_STATE) \ -X $(ROOT)/pkg/version.gitTag="$(shell git describe --tags --abbrev=0)" \ -X $(ROOT)/pkg/version.developmentFlag=true" \ - -race -v -timeout 20m -coverpkg=./pkg/... -coverprofile=e2e-lang-coverage.out ./e2e/v0/language + -race -v -timeout 20m -coverpkg=./pkg/... -coverprofile=e2e-lang-coverage.out ./e2e/language e2e-doc-test: @go test -ldflags "-s -w -X $(ROOT)/pkg/version.version=$(VERSION) \ @@ -210,34 +213,7 @@ e2e-doc-test: -X $(ROOT)/pkg/version.gitTreeState=$(GIT_TREE_STATE) \ -X $(ROOT)/pkg/version.gitTag="$(shell git describe --tags --abbrev=0)" \ -X $(ROOT)/pkg/version.developmentFlag=true" \ - -race -v -timeout 60m -coverpkg=./pkg/... -coverprofile=e2e-doc-coverage.out ./e2e/v0/docs - -e2e-cli-test-v1: - @go test -ldflags "-s -w -X $(ROOT)/pkg/version.version=$(VERSION) \ - -X $(ROOT)/pkg/version.buildDate=$(BUILD_DATE) \ - -X $(ROOT)/pkg/version.gitCommit=$(GIT_COMMIT) \ - -X $(ROOT)/pkg/version.gitTreeState=$(GIT_TREE_STATE) \ - -X $(ROOT)/pkg/version.gitTag="$(shell git describe --tags --abbrev=0)" \ - -X $(ROOT)/pkg/version.developmentFlag=true" \ - -race -v -timeout 20m -coverpkg=./pkg/... -coverprofile=e2e-cli-v1-coverage.out ./e2e/v1/cli - -e2e-lang-test-v1: - @go test -ldflags "-s -w -X $(ROOT)/pkg/version.version=$(VERSION) \ - -X $(ROOT)/pkg/version.buildDate=$(BUILD_DATE) \ - -X $(ROOT)/pkg/version.gitCommit=$(GIT_COMMIT) \ - -X $(ROOT)/pkg/version.gitTreeState=$(GIT_TREE_STATE) \ - -X $(ROOT)/pkg/version.gitTag="$(shell git describe --tags --abbrev=0)" \ - -X $(ROOT)/pkg/version.developmentFlag=true" \ - -race -v -timeout 20m -coverpkg=./pkg/... -coverprofile=e2e-lang-v1-coverage.out ./e2e/v1/language - -e2e-doc-test-v1: - @go test -ldflags "-s -w -X $(ROOT)/pkg/version.version=$(VERSION) \ - -X $(ROOT)/pkg/version.buildDate=$(BUILD_DATE) \ - -X $(ROOT)/pkg/version.gitCommit=$(GIT_COMMIT) \ - -X $(ROOT)/pkg/version.gitTreeState=$(GIT_TREE_STATE) \ - -X $(ROOT)/pkg/version.gitTag="$(shell git describe --tags --abbrev=0)" \ - -X $(ROOT)/pkg/version.developmentFlag=true" \ - -race -v -timeout 60m -coverpkg=./pkg/... -coverprofile=e2e-doc-v1-coverage.out ./e2e/v1/docs + -race -v -timeout 60m -coverpkg=./pkg/... -coverprofile=e2e-doc-coverage.out ./e2e/docs clean: ## Clean the outputs and artifacts @-rm -vrf ${OUTPUT_DIR} @@ -250,11 +226,12 @@ fmt: ## Run go fmt against code. vet: ## Run go vet against code. go vet ./... -black-install: - @pip install -q black[jupyter] +ruff-install: + @pip install -q ruff -envd-lint: black-install - black --check --include '(\.envd|\.py|\.ipynb)$$' . +envd-lint: ruff-install + @ruff check . -envd-fmt: black-install - black --include '(\.envd|\.py|\.ipynb)$$' . +envd-fmt: ruff-install + @ruff format . + @ruff check --fix . diff --git a/README.md b/README.md index 42cbb5524..69aee51f5 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,11 @@ </div> <p align=center> -<a href="https://discord.gg/KqswhpVgdU"><img alt="discord invitation link" src="https://dcbadge.vercel.app/api/server/KqswhpVgdU?style=flat"></a> -<a href="https://twitter.com/TensorChord"><img src="https://img.shields.io/twitter/follow/tensorchord?style=social" alt="trackgit-views" /></a> +<a href="https://discord.gg/KqswhpVgdU"><img alt="discord invitation link" src="https://img.shields.io/discord/974584200327991326?style=flat&logo=discord&cacheSeconds=60"></a> +<a href="https://twitter.com/TensorChord"><img src="https://img.shields.io/twitter/follow/tensorchord?style=flat&logo=X&cacheSeconds=60" alt="trackgit-views" /></a> <a href="https://pypi.org/project/envd"><img src="https://img.shields.io/pypi/pyversions/envd" alt="Python Version" /></a> <a href="https://github.com/tensorchord/envd#contributors-"><img alt="all-contributors" src="https://img.shields.io/github/all-contributors/tensorchord/envd/main"></a> -<a href="https://pypi.org/project/envd/"><img alt="envd package donwloads" src="https://static.pepy.tech/personalized-badge/envd?period=month&units=international_system&left_color=grey&right_color=brightgreen&left_text=downloads/month"</a> +<a href="https://pypi.org/project/envd/"><img alt="envd package downloads" src="https://static.pepy.tech/personalized-badge/envd?period=month&units=international_system&left_color=grey&right_color=brightgreen&left_text=downloads/month"</a> <a href="https://github.com/tensorchord/envd/actions/workflows/CI.yml"><img alt="continuous integration" src="https://github.com/tensorchord/envd/actions/workflows/CI.yml/badge.svg"></a> <a href='https://coveralls.io/github/tensorchord/envd?branch=main'><img src='https://coveralls.io/repos/github/tensorchord/envd/badge.svg?branch=main' alt='Coverage Status' /></a> </p> @@ -37,10 +37,13 @@ Environments built with `envd` provide the following features out-of-the-box: ```python def build(): + base(dev=True) + install.conda() + install.python() install.python_packages(name = [ "numpy", ]) - shell("zsh") + shell("fish") config.jupyter() ``` @@ -92,7 +95,9 @@ Forget copy-pasting Dockerfile instructions - use envd to easily build functions envdlib = include("https://github.com/tensorchord/envdlib") def build(): - base(os="ubuntu20.04", language="python") + base(dev=True) + install.conda() + install.python() envdlib.tensorboard(host_port=8888) ``` @@ -147,7 +152,7 @@ def tensorboard( `envd` can be installed with `pip`, or you can download the binary [release](https://github.com/tensorchord/envd/releases) directly. After the installation, please run `envd bootstrap` to bootstrap. ```bash -pip3 install --upgrade envd +pip install --upgrade envd ``` After the installation, please run `envd bootstrap` to bootstrap: @@ -176,13 +181,15 @@ The build manifest `build.envd` looks like: ```python title=build.envd def build(): - base(os="ubuntu20.04", language="python3") + base(dev=True) + install.conda() + install.python() # Configure the pip index if needed. # config.pip_index(url = "https://pypi.tuna.tsinghua.edu.cn/simple") install.python_packages(name = [ "numpy", ]) - shell("zsh") + shell("fish") ``` *Note that we use Python here as an example but please check out examples for other languages such as R and Julia [here](https://github.com/tensorchord/envd/tree/main/examples).* @@ -195,29 +202,34 @@ cd envd-quick-start && envd up ```bash $ cd envd-quick-start && envd up -[+] โŒš parse build.envd and download/cache dependencies 2.8s โœ… (finished) - => download oh-my-zsh 2.8s -[+] ๐Ÿ‹ build envd environment 18.3s (25/25) โœ… (finished) - => create apt source dir 0.0s - => local://cache-dir 0.1s - => => transferring cache-dir: 5.12MB 0.1s -... - => pip install numpy 13.0s - => copy /oh-my-zsh /home/envd/.oh-my-zsh 0.1s - => mkfile /home/envd/install.sh 0.0s - => install oh-my-zsh 0.1s - => mkfile /home/envd/.zshrc 0.0s - => install shell 0.0s - => install PyPI packages 0.0s - => merging all components into one 0.3s - => => merging 0.3s - => mkfile /home/envd/.gitconfig 0.0s - => exporting to oci image format 2.4s - => => exporting layers 2.0s - => => exporting manifest sha256:7dbe9494d2a7a39af16d514b997a5a8f08b637f 0.0s - => => exporting config sha256:1da06b907d53cf8a7312c138c3221e590dedc2717 0.0s - => => sending tarball 0.4s -envd-quick-start via Py v3.9.13 via ๐Ÿ…’ envd +[+] โŒš parse build.envd and download/cache dependencies 6.2s โœ… (finished) +[+] build envd environment 19.0s (47/47) FINISHED + => CACHED [internal] setting pip cache mount permissions 0.0s + => docker-image://docker.io/tensorchord/envd-sshd-from-scratch:v0.4.3 2.3s + => => resolve docker.io/tensorchord/envd-sshd-from-scratch:v0.4.3 2.3s + => docker-image://docker.io/library/ubuntu:22.04 0.0s +...... + => [internal] pip install numpy 2.5s + => CACHED [internal] download fish shell 0.0s + => [internal] configure user permissions for /opt/conda 1.0s + => [internal] create dir for ssh key 0.5s + => [internal] install ssh keys 0.2s + => [internal] copy fish shell from the builder image 0.2s + => [internal] install fish shell 0.5s +...... + => [internal] create work dir: /home/envd/envd-quick-start 0.2s + => exporting to image 7.7s + => => exporting layers 7.7s + => => writing image sha256:464a0c12759d3d1732404f217d5c6e06d0ee4890cccd66391a608daf2bd314e4 0.0s + => => naming to docker.io/library/envd-quick-start:dev 0.0s +------ + > importing cache manifest from docker.io/tensorchord/python-cache:envd-v0.4.3: +------ +โฃฝ [5/5] attach the environment [2s] +Welcome to fish, the friendly interactive shell +Type help for instructions on how to use fish + +envd-quick-start on git master [!] via Py v3.11.11 via ๐Ÿ…’ envd as sudo โฌข [envd]โฏ # You are in the container-based environment! ``` @@ -227,13 +239,15 @@ Please edit the `build.envd` to enable jupyter notebook: ```python title=build.envd def build(): - base(os="ubuntu20.04", language="python3") + base(dev=True) + install.conda() + install.python() # Configure the pip index if needed. # config.pip_index(url = "https://pypi.tuna.tsinghua.edu.cn/simple") install.python_packages(name = [ "numpy", ]) - shell("zsh") + shell("fish") config.jupyter() ``` @@ -246,6 +260,24 @@ NAME JUPYTER SSH TARGET CONTEXT envd-quick-start http://localhost:42779 envd-quick-start.envd /home/gaocegege/code/envd-quick-start envd-quick-start:dev false <none> <none> Up 54 seconds bd3f6a729e94 ``` +## Difference between v0 and v1 syntax + +> [!NOTE] +> Start from `envd v1.0`, `v1` syntax is the default syntax for `build.envd` file, and `moby-worker` is the default builder. + +| Features | v0 | v1 | +| --- | --- | --- | +| is default for `envd<v1.0` | โœ… | โŒ | +| support dev | โœ… | โœ… | +| support CUDA | โœ… | โœ… | +| support serving | โš ๏ธ | โœ… | +| support custom base image | โš ๏ธ | โœ… | +| support installing multiple languages | โš ๏ธ | โœ… | +| support `moby` builder | โŒ | โœ… | + +> [!IMPORTANT] +> For more details, check the [upgrade to v1](https://envd.tensorchord.ai/guide/v1.html) doc. + ## More on documentation ๐Ÿ“ See [envd documentation](https://envd.tensorchord.ai/guide/getting-started.html). @@ -294,60 +326,66 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d <td align="center" valign="top" width="14.28%"><a href="https://github.com/knight42"><img src="https://avatars.githubusercontent.com/u/4237254?v=4?s=70" width="70px;" alt="Jian Zeng"/><br /><sub><b>Jian Zeng</b></sub></a><br /><a href="#design-knight42" title="Design">๐ŸŽจ</a> <a href="#ideas-knight42" title="Ideas, Planning, & Feedback">๐Ÿค”</a> <a href="#research-knight42" title="Research">๐Ÿ”ฌ</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/VoVAllen"><img src="https://avatars.githubusercontent.com/u/8686776?v=4?s=70" width="70px;" alt="Jinjing Zhou"/><br /><sub><b>Jinjing Zhou</b></sub></a><br /><a href="https://github.com/tensorchord/envd/issues?q=author%3AVoVAllen" title="Bug reports">๐Ÿ›</a> <a href="https://github.com/tensorchord/envd/commits?author=VoVAllen" title="Code">๐Ÿ’ป</a> <a href="#design-VoVAllen" title="Design">๐ŸŽจ</a> <a href="https://github.com/tensorchord/envd/commits?author=VoVAllen" title="Documentation">๐Ÿ“–</a></td> <td align="center" valign="top" width="14.28%"><a href="http://jun.dev/blog/issues"><img src="https://avatars.githubusercontent.com/u/8097526?v=4?s=70" width="70px;" alt="Jun"/><br /><sub><b>Jun</b></sub></a><br /><a href="#platform-junnplus" title="Packaging/porting to new platform">๐Ÿ“ฆ</a> <a href="https://github.com/tensorchord/envd/commits?author=junnplus" title="Code">๐Ÿ’ป</a></td> + <td align="center" valign="top" width="14.28%"><a href="https://github.com/Kaiyang-Chen"><img src="https://avatars.githubusercontent.com/u/48289729?v=4?s=70" width="70px;" alt="Kaiyang Chen"/><br /><sub><b>Kaiyang Chen</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=Kaiyang-Chen" title="Code">๐Ÿ’ป</a></td> <td align="center" valign="top" width="14.28%"><a href="https://kemingy.github.io/"><img src="https://avatars.githubusercontent.com/u/12974685?v=4?s=70" width="70px;" alt="Keming"/><br /><sub><b>Keming</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=kemingy" title="Code">๐Ÿ’ป</a> <a href="https://github.com/tensorchord/envd/commits?author=kemingy" title="Documentation">๐Ÿ“–</a> <a href="#ideas-kemingy" title="Ideas, Planning, & Feedback">๐Ÿค”</a> <a href="#infra-kemingy" title="Infrastructure (Hosting, Build-Tools, etc)">๐Ÿš‡</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/pingsutw"><img src="https://avatars.githubusercontent.com/u/37936015?v=4?s=70" width="70px;" alt="Kevin Su"/><br /><sub><b>Kevin Su</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=pingsutw" title="Code">๐Ÿ’ป</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/3AceShowHand"><img src="https://avatars.githubusercontent.com/u/7138436?v=4?s=70" width="70px;" alt="Ling Jin"/><br /><sub><b>Ling Jin</b></sub></a><br /><a href="https://github.com/tensorchord/envd/issues?q=author%3A3AceShowHand" title="Bug reports">๐Ÿ›</a> <a href="#infra-3AceShowHand" title="Infrastructure (Hosting, Build-Tools, etc)">๐Ÿš‡</a></td> - <td align="center" valign="top" width="14.28%"><a href="http://manjusaka.itscoder.com"><img src="https://avatars.githubusercontent.com/u/7054676?v=4?s=70" width="70px;" alt="Manjusaka"/><br /><sub><b>Manjusaka</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=Zheaoli" title="Code">๐Ÿ’ป</a></td> </tr> <tr> + <td align="center" valign="top" width="14.28%"><a href="http://manjusaka.itscoder.com"><img src="https://avatars.githubusercontent.com/u/7054676?v=4?s=70" width="70px;" alt="Manjusaka"/><br /><sub><b>Manjusaka</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=Zheaoli" title="Code">๐Ÿ’ป</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/lilylee1874"><img src="https://avatars.githubusercontent.com/u/52693877?v=4?s=70" width="70px;" alt="Nino"/><br /><sub><b>Nino</b></sub></a><br /><a href="#design-lilylee1874" title="Design">๐ŸŽจ</a> <a href="https://github.com/tensorchord/envd/commits?author=lilylee1874" title="Code">๐Ÿ’ป</a></td> <td align="center" valign="top" width="14.28%"><a href="http://phillipw.info"><img src="https://avatars.githubusercontent.com/u/34707116?v=4?s=70" width="70px;" alt="Pengyu Wang"/><br /><sub><b>Pengyu Wang</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=cswpy" title="Documentation">๐Ÿ“–</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/Sepush"><img src="https://avatars.githubusercontent.com/u/39197136?v=4?s=70" width="70px;" alt="Sepush"/><br /><sub><b>Sepush</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=sepush" title="Documentation">๐Ÿ“–</a></td> + <td align="center" valign="top" width="14.28%"><a href="http://blog.electronicwaste.cn"><img src="https://avatars.githubusercontent.com/u/77665902?v=4?s=70" width="70px;" alt="Shao Wang"/><br /><sub><b>Shao Wang</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=Electronic-Waste" title="Code">๐Ÿ’ป</a></td> <td align="center" valign="top" width="14.28%"><a href="https://blog.thrimbda.com/"><img src="https://avatars.githubusercontent.com/u/15231162?v=4?s=70" width="70px;" alt="Siyuan Wang"/><br /><sub><b>Siyuan Wang</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=Thrimbda" title="Code">๐Ÿ’ป</a> <a href="#infra-Thrimbda" title="Infrastructure (Hosting, Build-Tools, etc)">๐Ÿš‡</a> <a href="#maintenance-Thrimbda" title="Maintenance">๐Ÿšง</a></td> <td align="center" valign="top" width="14.28%"><a href="http://suyan.moe"><img src="https://avatars.githubusercontent.com/u/24221472?v=4?s=70" width="70px;" alt="Suyan"/><br /><sub><b>Suyan</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=suyanhanx" title="Documentation">๐Ÿ“–</a></td> - <td align="center" valign="top" width="14.28%"><a href="http://myfra.vercel.app"><img src="https://avatars.githubusercontent.com/u/60420319?v=4?s=70" width="70px;" alt="To My"/><br /><sub><b>To My</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=MyFRA" title="Documentation">๐Ÿ“–</a></td> - <td align="center" valign="top" width="14.28%"><a href="https://www.iam.rw"><img src="https://avatars.githubusercontent.com/u/29533246?v=4?s=70" width="70px;" alt="Tumushimire Yves"/><br /><sub><b>Tumushimire Yves</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=yvestumushimire" title="Code">๐Ÿ’ป</a></td> </tr> <tr> + <td align="center" valign="top" width="14.28%"><a href="http://myfra.vercel.app"><img src="https://avatars.githubusercontent.com/u/60420319?v=4?s=70" width="70px;" alt="To My"/><br /><sub><b>To My</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=MyFRA" title="Documentation">๐Ÿ“–</a></td> + <td align="center" valign="top" width="14.28%"><a href="https://www.iam.rw"><img src="https://avatars.githubusercontent.com/u/29533246?v=4?s=70" width="70px;" alt="Tumushimire Yves"/><br /><sub><b>Tumushimire Yves</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=yvestumushimire" title="Code">๐Ÿ’ป</a></td> <td align="center" valign="top" width="14.28%"><a href="https://page.codespaper.com"><img src="https://avatars.githubusercontent.com/u/3764335?v=4?s=70" width="70px;" alt="Wei Zhang"/><br /><sub><b>Wei Zhang</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=zwpaper" title="Code">๐Ÿ’ป</a></td> <td align="center" valign="top" width="14.28%"><a href="http://weixiao-huang.github.io"><img src="https://avatars.githubusercontent.com/u/8834733?v=4?s=70" width="70px;" alt="Weixiao Huang"/><br /><sub><b>Weixiao Huang</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=weixiao-huang" title="Code">๐Ÿ’ป</a></td> <td align="center" valign="top" width="14.28%"><a href="https://www.hawkingrei.com/"><img src="https://avatars.githubusercontent.com/u/3427324?v=4?s=70" width="70px;" alt="Weizhen Wang"/><br /><sub><b>Weizhen Wang</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=hawkingrei" title="Code">๐Ÿ’ป</a></td> <td align="center" valign="top" width="14.28%"><a href="http://blog.xuruowei.com"><img src="https://avatars.githubusercontent.com/u/18398013?v=4?s=70" width="70px;" alt="XRW"/><br /><sub><b>XRW</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=Xuruowei" title="Code">๐Ÿ’ป</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/jiayouxujin"><img src="https://avatars.githubusercontent.com/u/29749249?v=4?s=70" width="70px;" alt="Xu Jin"/><br /><sub><b>Xu Jin</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=jiayouxujin" title="Code">๐Ÿ’ป</a></td> - <td align="center" valign="top" width="14.28%"><a href="https://xuanwo.io/"><img src="https://avatars.githubusercontent.com/u/5351546?v=4?s=70" width="70px;" alt="Xuanwo"/><br /><sub><b>Xuanwo</b></sub></a><br /><a href="#question-Xuanwo" title="Answering Questions">๐Ÿ’ฌ</a> <a href="#design-Xuanwo" title="Design">๐ŸŽจ</a> <a href="#ideas-Xuanwo" title="Ideas, Planning, & Feedback">๐Ÿค”</a> <a href="https://github.com/tensorchord/envd/pulls?q=is%3Apr+reviewed-by%3AXuanwo" title="Reviewed Pull Requests">๐Ÿ‘€</a></td> - <td align="center" valign="top" width="14.28%"><a href="https://github.com/enjoyliu"><img src="https://avatars.githubusercontent.com/u/13460894?v=4?s=70" width="70px;" alt="Yijiang Liu"/><br /><sub><b>Yijiang Liu</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=enjoyliu" title="Code">๐Ÿ’ป</a></td> </tr> <tr> + <td align="center" valign="top" width="14.28%"><a href="https://xuanwo.io/"><img src="https://avatars.githubusercontent.com/u/5351546?v=4?s=70" width="70px;" alt="Xuanwo"/><br /><sub><b>Xuanwo</b></sub></a><br /><a href="#question-Xuanwo" title="Answering Questions">๐Ÿ’ฌ</a> <a href="#design-Xuanwo" title="Design">๐ŸŽจ</a> <a href="#ideas-Xuanwo" title="Ideas, Planning, & Feedback">๐Ÿค”</a> <a href="https://github.com/tensorchord/envd/pulls?q=is%3Apr+reviewed-by%3AXuanwo" title="Reviewed Pull Requests">๐Ÿ‘€</a></td> + <td align="center" valign="top" width="14.28%"><a href="https://github.com/enjoyliu"><img src="https://avatars.githubusercontent.com/u/13460894?v=4?s=70" width="70px;" alt="Yijiang Liu"/><br /><sub><b>Yijiang Liu</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=enjoyliu" title="Code">๐Ÿ’ป</a></td> <td align="center" valign="top" width="14.28%"><a href="https://elon.fun/"><img src="https://avatars.githubusercontent.com/u/8433465?v=4?s=70" width="70px;" alt="Yilong Li"/><br /><sub><b>Yilong Li</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=dragonly" title="Documentation">๐Ÿ“–</a> <a href="https://github.com/tensorchord/envd/issues?q=author%3Adragonly" title="Bug reports">๐Ÿ›</a> <a href="https://github.com/tensorchord/envd/commits?author=dragonly" title="Code">๐Ÿ’ป</a></td> <td align="center" valign="top" width="14.28%"><a href="https://terrytangyuan.github.io/about/"><img src="https://avatars.githubusercontent.com/u/4269898?v=4?s=70" width="70px;" alt="Yuan Tang"/><br /><sub><b>Yuan Tang</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=terrytangyuan" title="Code">๐Ÿ’ป</a> <a href="#design-terrytangyuan" title="Design">๐ŸŽจ</a> <a href="https://github.com/tensorchord/envd/commits?author=terrytangyuan" title="Documentation">๐Ÿ“–</a> <a href="#ideas-terrytangyuan" title="Ideas, Planning, & Feedback">๐Ÿค”</a></td> <td align="center" valign="top" width="14.28%"><a href="https://rudeigerc.dev/"><img src="https://avatars.githubusercontent.com/u/18243819?v=4?s=70" width="70px;" alt="Yuchen Cheng"/><br /><sub><b>Yuchen Cheng</b></sub></a><br /><a href="https://github.com/tensorchord/envd/issues?q=author%3Arudeigerc" title="Bug reports">๐Ÿ›</a> <a href="#infra-rudeigerc" title="Infrastructure (Hosting, Build-Tools, etc)">๐Ÿš‡</a> <a href="#maintenance-rudeigerc" title="Maintenance">๐Ÿšง</a> <a href="#tool-rudeigerc" title="Tools">๐Ÿ”ง</a></td> <td align="center" valign="top" width="14.28%"><a href="http://lunarwhite.github.io"><img src="https://avatars.githubusercontent.com/u/57584831?v=4?s=70" width="70px;" alt="Yuedong Wu"/><br /><sub><b>Yuedong Wu</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=lunarwhite" title="Code">๐Ÿ’ป</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/yczheng0"><img src="https://avatars.githubusercontent.com/u/21327543?v=4?s=70" width="70px;" alt="Yunchuan Zheng"/><br /><sub><b>Yunchuan Zheng</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=yczheng0" title="Code">๐Ÿ’ป</a></td> - <td align="center" valign="top" width="14.28%"><a href="http://lizheming.top"><img src="https://avatars.githubusercontent.com/u/9639449?v=4?s=70" width="70px;" alt="Zheming Li"/><br /><sub><b>Zheming Li</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=lizhemingi" title="Code">๐Ÿ’ป</a></td> - <td align="center" valign="top" width="14.28%"><a href="https://github.com/Xiaoaier-Z-L"><img src="https://avatars.githubusercontent.com/u/96805673?v=4?s=70" width="70px;" alt="Zhenguo.Li"/><br /><sub><b>Zhenguo.Li</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=Xiaoaier-Z-L" title="Code">๐Ÿ’ป</a> <a href="https://github.com/tensorchord/envd/commits?author=Xiaoaier-Z-L" title="Documentation">๐Ÿ“–</a></td> </tr> <tr> + <td align="center" valign="top" width="14.28%"><a href="http://lizheming.top"><img src="https://avatars.githubusercontent.com/u/9639449?v=4?s=70" width="70px;" alt="Zheming Li"/><br /><sub><b>Zheming Li</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=lizhemingi" title="Code">๐Ÿ’ป</a></td> + <td align="center" valign="top" width="14.28%"><a href="https://github.com/Xiaoaier-Z-L"><img src="https://avatars.githubusercontent.com/u/96805673?v=4?s=70" width="70px;" alt="Zhenguo.Li"/><br /><sub><b>Zhenguo.Li</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=Xiaoaier-Z-L" title="Code">๐Ÿ’ป</a> <a href="https://github.com/tensorchord/envd/commits?author=Xiaoaier-Z-L" title="Documentation">๐Ÿ“–</a></td> <td align="center" valign="top" width="14.28%"><a href="https://blog.triplez.cn/"><img src="https://avatars.githubusercontent.com/u/16285716?v=4?s=70" width="70px;" alt="Zhenzhen Zhao"/><br /><sub><b>Zhenzhen Zhao</b></sub></a><br /><a href="#infra-Triple-Z" title="Infrastructure (Hosting, Build-Tools, etc)">๐Ÿš‡</a> <a href="#userTesting-Triple-Z" title="User Testing">๐Ÿ““</a> <a href="https://github.com/tensorchord/envd/commits?author=Triple-Z" title="Code">๐Ÿ’ป</a></td> <td align="center" valign="top" width="14.28%"><a href="https://t.me/littlepoint"><img src="https://avatars.githubusercontent.com/u/7611700?v=4?s=70" width="70px;" alt="Zhizhen He"/><br /><sub><b>Zhizhen He</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=hezhizhen" title="Code">๐Ÿ’ป</a> <a href="https://github.com/tensorchord/envd/commits?author=hezhizhen" title="Documentation">๐Ÿ“–</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/cutecutecat"><img src="https://avatars.githubusercontent.com/u/19801166?v=4?s=70" width="70px;" alt="cutecutecat"/><br /><sub><b>cutecutecat</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=cutecutecat" title="Code">๐Ÿ’ป</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/dqhl76"><img src="https://avatars.githubusercontent.com/u/69136320?v=4?s=70" width="70px;" alt="dqhl76"/><br /><sub><b>dqhl76</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=dqhl76" title="Documentation">๐Ÿ“–</a> <a href="https://github.com/tensorchord/envd/commits?author=dqhl76" title="Code">๐Ÿ’ป</a></td> + <td align="center" valign="top" width="14.28%"><a href="https://lxb1226.github.io/"><img src="https://avatars.githubusercontent.com/u/33415192?v=4?s=70" width="70px;" alt="heyjude"/><br /><sub><b>heyjude</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=lxb1226" title="Code">๐Ÿ’ป</a></td> + </tr> + <tr> <td align="center" valign="top" width="14.28%"><a href="https://github.com/jimoosciuc"><img src="https://avatars.githubusercontent.com/u/33337387?v=4?s=70" width="70px;" alt="jimoosciuc"/><br /><sub><b>jimoosciuc</b></sub></a><br /><a href="#userTesting-jimoosciuc" title="User Testing">๐Ÿ““</a></td> <td align="center" valign="top" width="14.28%"><a href="https://kenwoodjw.github.io"><img src="https://avatars.githubusercontent.com/u/10386710?v=4?s=70" width="70px;" alt="kenwoodjw"/><br /><sub><b>kenwoodjw</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=kenwoodjw" title="Code">๐Ÿ’ป</a></td> <td align="center" valign="top" width="14.28%"><a href="http://www.hwdef.org"><img src="https://avatars.githubusercontent.com/u/13084946?v=4?s=70" width="70px;" alt="li mengyang"/><br /><sub><b>li mengyang</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=hwdef" title="Code">๐Ÿ’ป</a></td> - </tr> - <tr> <td align="center" valign="top" width="14.28%"><a href="https://github.com/aseaday"><img src="https://avatars.githubusercontent.com/u/3927355?v=4?s=70" width="70px;" alt="nullday"/><br /><sub><b>nullday</b></sub></a><br /><a href="#ideas-aseaday" title="Ideas, Planning, & Feedback">๐Ÿค”</a> <a href="https://github.com/tensorchord/envd/commits?author=aseaday" title="Code">๐Ÿ’ป</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/rrain7"><img src="https://avatars.githubusercontent.com/u/49144127?v=4?s=70" width="70px;" alt="rrain7"/><br /><sub><b>rrain7</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=rrain7" title="Code">๐Ÿ’ป</a></td> <td align="center" valign="top" width="14.28%"><a href="https://tisonkun.org/"><img src="https://avatars.githubusercontent.com/u/18818196?v=4?s=70" width="70px;" alt="tison"/><br /><sub><b>tison</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=tisonkun" title="Code">๐Ÿ’ป</a></td> <td align="center" valign="top" width="14.28%"><a href="http://fatelei.github.io"><img src="https://avatars.githubusercontent.com/u/961094?v=4?s=70" width="70px;" alt="wangxiaolei"/><br /><sub><b>wangxiaolei</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=fatelei" title="Code">๐Ÿ’ป</a></td> + </tr> + <tr> <td align="center" valign="top" width="14.28%"><a href="https://github.com/sea-wyq"><img src="https://avatars.githubusercontent.com/u/22475606?v=4?s=70" width="70px;" alt="wyq"/><br /><sub><b>wyq</b></sub></a><br /><a href="https://github.com/tensorchord/envd/issues?q=author%3Asea-wyq" title="Bug reports">๐Ÿ›</a> <a href="#design-sea-wyq" title="Design">๐ŸŽจ</a> <a href="https://github.com/tensorchord/envd/commits?author=sea-wyq" title="Code">๐Ÿ’ป</a></td> <td align="center" valign="top" width="14.28%"><a href="https://oubotong.github.io/johan"><img src="https://avatars.githubusercontent.com/u/26356127?v=4?s=70" width="70px;" alt="x0oo0x"/><br /><sub><b>x0oo0x</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=oubotong" title="Code">๐Ÿ’ป</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/xiangtianyu"><img src="https://avatars.githubusercontent.com/u/10825900?v=4?s=70" width="70px;" alt="xiangtianyu"/><br /><sub><b>xiangtianyu</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=xiangtianyu" title="Documentation">๐Ÿ“–</a></td> - </tr> - <tr> <td align="center" valign="top" width="14.28%"><a href="https://github.com/xieydd"><img src="https://avatars.githubusercontent.com/u/20329697?v=4?s=70" width="70px;" alt="xieydd"/><br /><sub><b>xieydd</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=xieydd" title="Code">๐Ÿ’ป</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/xing0821"><img src="https://avatars.githubusercontent.com/u/54933318?v=4?s=70" width="70px;" alt="xing0821"/><br /><sub><b>xing0821</b></sub></a><br /><a href="#ideas-xing0821" title="Ideas, Planning, & Feedback">๐Ÿค”</a> <a href="#userTesting-xing0821" title="User Testing">๐Ÿ““</a> <a href="https://github.com/tensorchord/envd/commits?author=xing0821" title="Code">๐Ÿ’ป</a></td> <td align="center" valign="top" width="14.28%"><a href="https://xxchan.github.io"><img src="https://avatars.githubusercontent.com/u/37948597?v=4?s=70" width="70px;" alt="xxchan"/><br /><sub><b>xxchan</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=xxchan" title="Documentation">๐Ÿ“–</a></td> + <td align="center" valign="top" width="14.28%"><a href="http://blogs.zhangwei.asia"><img src="https://avatars.githubusercontent.com/u/26432832?v=4?s=70" width="70px;" alt="zhang-wei"/><br /><sub><b>zhang-wei</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=arugal" title="Code">๐Ÿ’ป</a></td> + </tr> + <tr> <td align="center" valign="top" width="14.28%"><a href="https://github.com/zhyon404"><img src="https://avatars.githubusercontent.com/u/32242529?v=4?s=70" width="70px;" alt="zhyon404"/><br /><sub><b>zhyon404</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=zhyon404" title="Code">๐Ÿ’ป</a></td> <td align="center" valign="top" width="14.28%"><a href="https://www.homeboyc.cn/"><img src="https://avatars.githubusercontent.com/u/22193008?v=4?s=70" width="70px;" alt="ๆจๆˆ้”ด"/><br /><sub><b>ๆจๆˆ้”ด</b></sub></a><br /><a href="https://github.com/tensorchord/envd/commits?author=asjdf" title="Code">๐Ÿ’ป</a></td> </tr> diff --git a/base-images/.dockerignore b/base-images/.dockerignore deleted file mode 100644 index 9ad039d21..000000000 --- a/base-images/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -build.sh -*.Dockerfile diff --git a/base-images/build.sh b/base-images/build.sh deleted file mode 100755 index 8360039d2..000000000 --- a/base-images/build.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env bash -# Copyright 2022 The envd 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. - - -set -euo pipefail - -ROOT_DIR=`dirname $0` - -GIT_TAG_VERSION=$(git describe --tags --abbrev=0) -DOCKER_HUB_ORG="${DOCKER_HUB_ORG:-tensorchord}" -ENVD_OS="${ENVD_OS:-ubuntu20.04}" -JULIA_VERSION="${JULIA_VERSION:-1.8rc1}" -RLANG_VERSION="${RLANG_VERSION:-4.2}" - -cd ${ROOT_DIR} -# ubuntu 22.04 build require moby/buildkit version greater than 0.8.1 -docker buildx create --use --platform linux/x86_64,linux/arm64,linux/ppc64le --driver-opt image=moby/buildkit:v0.10.3 - -# https://github.com/docker/buildx/issues/495#issuecomment-754688157 -docker run --rm --privileged multiarch/qemu-user-static --reset -p yes - -# TODO(gaocegege): Support linux/arm64 -docker buildx build \ - --build-arg ENVD_VERSION=${GIT_TAG_VERSION} \ - --build-arg ENVD_SSHD_IMAGE=tensorchord/envd-sshd-from-scratch \ - -t ${DOCKER_HUB_ORG}/r-base:${RLANG_VERSION}-envd-${GIT_TAG_VERSION} \ - --pull --push --platform linux/x86_64 \ - -f r${RLANG_VERSION}.Dockerfile . -docker buildx build \ - --build-arg ENVD_VERSION=${GIT_TAG_VERSION} \ - --build-arg ENVD_SSHD_IMAGE=tensorchord/envd-sshd-from-scratch \ - -t ${DOCKER_HUB_ORG}/julia:${JULIA_VERSION}-${ENVD_OS}-envd-${GIT_TAG_VERSION} \ - --pull --push --platform linux/x86_64,linux/arm64 \ - -f julia${JULIA_VERSION}-${ENVD_OS}.Dockerfile . -cd - > /dev/null diff --git a/base-images/envd-starship/envd-starship.Dockerfile b/base-images/envd-starship/envd-starship.Dockerfile new file mode 100644 index 000000000..d478b47dd --- /dev/null +++ b/base-images/envd-starship/envd-starship.Dockerfile @@ -0,0 +1,6 @@ +FROM curlimages/curl:7.87.0 as builder +USER root +RUN curl --proto '=https' --tlsv1.2 -sSf https://starship.rs/install.sh | sh -s -- -y + +FROM scratch as prod +COPY --from=builder /usr/local/bin/starship /usr/local/bin/starship \ No newline at end of file diff --git a/base-images/julia1.8rc1-ubuntu20.04.Dockerfile b/base-images/julia1.8rc1-ubuntu20.04.Dockerfile deleted file mode 100644 index 0cdd309e5..000000000 --- a/base-images/julia1.8rc1-ubuntu20.04.Dockerfile +++ /dev/null @@ -1,98 +0,0 @@ -ARG ENVD_VERSION -ARG ENVD_SSHD_IMAGE -FROM ubuntu:20.04 as base - -FROM base as base-amd64 - -FROM base as base-arm64 - -FROM ${ENVD_SSHD_IMAGE}:${ENVD_VERSION} AS envd - -FROM base-${TARGETARCH} - -LABEL maintainer "envd-maintainers <envd-maintainers@tensorchord.ai>" - -ENV DEBIAN_FRONTEND noninteractive -ENV PATH="/usr/bin:${PATH}" -ENV LANG C.UTF-8 -ENV LC_ALL C.UTF-8 - -RUN apt-get update && \ - apt-get install -y --no-install-recommends --no-install-suggests --fix-missing \ - bash-static libtinfo5 libncursesw5 apt-utils \ - # conda dependencies - bzip2 ca-certificates libglib2.0-0 libsm6 libxext6 libxrender1 mercurial \ - procps subversion wget \ - # envd dependencies - python3 curl openssh-client git tini sudo python3-pip zsh vim \ - && rm -rf /var/lib/apt/lists/* \ - # prompt - && curl --proto '=https' --tlsv1.2 -sSf https://starship.rs/install.sh | sh -s -- -y - -COPY --from=envd /usr/bin/envd-sshd /var/envd/bin/envd-sshd - -ENV JULIA_PATH /usr/local/julia -ENV PATH $JULIA_PATH/bin:$PATH - -# https://julialang.org/juliareleases.asc -# Julia (Binary signing key) <buildbot@julialang.org> -ENV JULIA_GPG 3673DF529D9049477F76B37566E3C7DC03D6E495 - -# https://julialang.org/downloads/ -ENV JULIA_VERSION 1.8.0-rc1 - -RUN set -eux; \ - \ - savedAptMark="$(apt-mark showmanual)"; \ - if ! command -v gpg > /dev/null; then \ - apt-get update; \ - apt-get install -y --no-install-recommends \ - gnupg \ - dirmngr \ - ; \ - rm -rf /var/lib/apt/lists/*; \ - fi; \ - \ -# https://julialang.org/downloads/#julia-command-line-version -# https://julialang-s3.julialang.org/bin/checksums/julia-1.8.0-rc1.sha256 - arch="$(dpkg --print-architecture)"; \ - case "$arch" in \ - 'amd64') \ - url='https://julialang-s3.julialang.org/bin/linux/x64/1.8/julia-1.8.0-rc1-linux-x86_64.tar.gz'; \ - sha256='a47efddaaccb424dad6499f870ab7f792c50827d23cc64cb9873280318337966'; \ - ;; \ - 'arm64') \ - url='https://julialang-s3.julialang.org/bin/linux/aarch64/1.8/julia-1.8.0-rc1-linux-aarch64.tar.gz'; \ - sha256='15dd553754aa15e514f28ed00ed4cfdb1f8cf883f3398b803ef5cf05e767a2fb'; \ - ;; \ - 'i386') \ - url='https://julialang-s3.julialang.org/bin/linux/x86/1.8/julia-1.8.0-rc1-linux-i686.tar.gz'; \ - sha256='bed81bb5e2cd60abb824b40cbb1ed2f27c9f974dfd7fbc43ce1684e5462bae2b'; \ - ;; \ - *) \ - echo >&2 "error: current architecture ($arch) does not have a corresponding Julia binary release"; \ - exit 1; \ - ;; \ - esac; \ - \ - curl -fL -o julia.tar.gz.asc "$url.asc"; \ - curl -fL -o julia.tar.gz "$url"; \ - \ - echo "$sha256 *julia.tar.gz" | sha256sum --strict --check -; \ - \ - export GNUPGHOME="$(mktemp -d)"; \ - gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$JULIA_GPG"; \ - gpg --batch --verify julia.tar.gz.asc julia.tar.gz; \ - command -v gpgconf > /dev/null && gpgconf --kill all; \ - rm -rf "$GNUPGHOME" julia.tar.gz.asc; \ - \ - mkdir "$JULIA_PATH"; \ - tar -xzf julia.tar.gz -C "$JULIA_PATH" --strip-components 1; \ - rm julia.tar.gz; \ - \ - apt-mark auto '.*' > /dev/null; \ - [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; \ - apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ - \ -# smoke test - julia --version diff --git a/base-images/r4.2.Dockerfile b/base-images/r4.2.Dockerfile deleted file mode 100644 index 1a05f2d4a..000000000 --- a/base-images/r4.2.Dockerfile +++ /dev/null @@ -1,46 +0,0 @@ -ARG ENVD_VERSION -ARG ENVD_SSHD_IMAGE -FROM rocker/r-ver:4.2 as base - -FROM base as base-amd64 - -FROM base as base-arm64 - -FROM ${ENVD_SSHD_IMAGE}:${ENVD_VERSION} AS envd - -FROM base-${TARGETARCH} - -ARG TARGETARCH - -LABEL maintainer "envd-maintainers <envd-maintainers@tensorchord.ai>" - -ENV DEBIAN_FRONTEND noninteractive -ENV PATH="/usr/bin:${PATH}" -ENV LANG C.UTF-8 -ENV LC_ALL C.UTF-8 - -RUN apt-get update && \ - apt-get install -y --no-install-recommends --no-install-suggests --fix-missing \ - apt-utils bash-static libtinfo5 libncursesw5 \ - # rstudio dependencies - file libapparmor1 libclang-dev libcurl4-openssl-dev libedit2 libobjc4 wget libssl-dev \ - libpq5 psmisc procps python3-setuptools pwgen lsb-release \ - # envd dependencies - python3 curl openssh-client git tini sudo zsh vim \ - && rm -rf /var/lib/apt/lists/* \ - # prompt - && curl --proto '=https' --tlsv1.2 -sSf https://starship.rs/install.sh | sh -s -- -y - -RUN set -x && \ - UNAME_M="$(uname -m)" && \ - if [ "${UNAME_M}" = "x86_64" ]; then \ - RSTUDIO_URL="https://download2.rstudio.org/server/jammy/amd64/rstudio-server-2022.12.0-353-amd64.deb"; \ - elif [ "${UNAME_M}" = "aarch64" ]; then \ - RSTUDIO_URL="https://rstudio.org/download/latest/latest/server/focal/rstudio-server-latest-arm64.deb"; \ - fi && \ - DOWNLOAD_FILE=rstudio-server.deb && \ - wget "${RSTUDIO_URL}" -O ${DOWNLOAD_FILE} && \ - dpkg -i "$DOWNLOAD_FILE" && \ - rm ${DOWNLOAD_FILE} && rm -f /var/lib/rstudio-server/secure-cookie-key - -COPY --from=envd /usr/bin/envd-sshd /var/envd/bin/envd-sshd diff --git a/base-images/remote-cache/build-and-push-remote-cache.sh b/base-images/remote-cache/build-and-push-remote-cache.sh index a48ce7a76..15c804e02 100755 --- a/base-images/remote-cache/build-and-push-remote-cache.sh +++ b/base-images/remote-cache/build-and-push-remote-cache.sh @@ -26,6 +26,7 @@ TAG_SUFFIX="${TAG_SUFFIX:-}" cd ${ROOT_DIR} +envd context create --name docker --builder docker-container --use envd --debug build -f build.envd:${BUILD_FUNC} --export-cache type=registry,ref=docker.io/${DOCKER_HUB_ORG}/python-cache:envd-v${ENVD_VERSION}${TAG_SUFFIX} --force cd - > /dev/null diff --git a/base-images/remote-cache/build.envd b/base-images/remote-cache/build.envd index 43591d86a..af5d40e3f 100644 --- a/base-images/remote-cache/build.envd +++ b/base-images/remote-cache/build.envd @@ -1,20 +1,29 @@ def build(): - base(os="ubuntu20.04", language="python3") + """default""" + base(dev=True) + install.conda() + install.python() -def build_gpu_11_2(): - """tensorflow""" - base(os="ubuntu20.04", language="python3") - install.cuda(version="11.2.2", cudnn="8") +def build_gpu_11_8(): + """cuda 11""" + base(dev=True) + install.conda() + install.python() + install.cuda(version="11.8.0", cudnn="8") -def build_gpu_11_3(): - """pytorch""" - base(os="ubuntu20.04", language="python3") - install.cuda(version="11.3.1", cudnn="8") +def build_gpu_12_2(): + """cuda 12""" + base(dev=True) + install.conda() + install.python() + install.cuda(version="12.2.2", cudnn="8") -def build_gpu_11_6(): - """pytorch""" - base(os="ubuntu20.04", language="python3") - install.cuda(version="11.6.2", cudnn="8") +def build_gpu_12_3(): + """cudnn 9""" + base(dev=True) + install.conda() + install.python() + install.cuda(version="12.3.2", cudnn="9") diff --git a/cmd/envd-sshd/main.go b/cmd/envd-sshd/main.go index 3840c9b93..ed47ee313 100644 --- a/cmd/envd-sshd/main.go +++ b/cmd/envd-sshd/main.go @@ -1,5 +1,5 @@ -// Copyright 2022 The envd Authors -// Copyright 2022 The okteto remote Authors +// Copyright 2023 The envd Authors +// Copyright 2023 The okteto remote Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -103,14 +103,15 @@ func main() { func sshServer(c *cli.Context) error { err := sshd.GetShell(c.String(flagShell)) if err != nil { - logrus.Fatal(err.Error()) + logrus.WithError(err).Fatal() } shell := c.String(flagShell) port := c.Int(flagPort) if port == 0 { return errors.New("port must be set") - } else if port <= 1024 { + } + if port <= 1024 { return errors.New("failed to parse port: port is reserved") } @@ -141,12 +142,12 @@ func sshServer(c *cli.Context) error { return errors.Wrapf( err, "reading private key %s failed", c.String(flagHostKey)) } - if privateKey, err := rawssh.ParsePrivateKey(pemBytes); err != nil { + privateKey, err := rawssh.ParsePrivateKey(pemBytes) + if err != nil { return err - } else { - logrus.Debugf("load host key from %s", c.String(flagHostKey)) - hostKey = privateKey } + logrus.Debugf("load host key from %s", c.String(flagHostKey)) + hostKey = privateKey } srv := sshd.Server{ diff --git a/cmd/envd/main.go b/cmd/envd/main.go index 309e7d520..6915194fa 100644 --- a/cmd/envd/main.go +++ b/cmd/envd/main.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/docs/images/after.svg b/docs/images/after.svg deleted file mode 100644 index 5741ce13d..000000000 --- a/docs/images/after.svg +++ /dev/null @@ -1,3 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="705px" height="410px" viewBox="-0.5 -0.5 705 410"><defs/><g><path d="M 108 52 Q 145.5 52 145.5 52.5 Q 145.5 53 172.9 53" fill="none" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" stroke-dasharray="9 9" pointer-events="stroke"/><path d="M 179.65 53 L 170.65 57.5 L 172.9 53 L 170.65 48.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/><image x="39.5" y="0.5" width="68" height="68" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAAAXNSR0IArs4c6QAAEPpJREFUeF7tXWuUXFWV/vapdHdIhyGJAaNrDIgCUdRlBAEZwbDk0VUdQyR1Ky8SnqZu5YEwAQdRnOAM4/BQI5l0VTWYoDyS1K0EVpK61QE0QWQJxvhYgIYomBWdIRnzUEg6ne6us2ed7sSJCVLnVt1769bj/ukf/e199v7OV/dxHvsQGlddM0B1nX0jeTQEUOciaAigIYA6Z6DO02/cARoCqHMG6jz9xh2gIYA6Z6DO02/cARoCqC8GPjd9zmjZHzoVhJEsZavKnoQ4QFTYSwI71q3o3F1PjNT0HWDChEVDho/e+ekC0SUATyDgIwBGFengvQy8TISNzLTx4J/e/fymTYv6a1UUNSmAtimJjwnB1wM8HaBTyuk8ZuyC4BXEvMy2Ol8qx1cQbWtKABON+Kck8BWAIurO7jLhDNB6yMLd9urOF132XTF3bpNUkUQixtwxEv33CoirPej4t8mJ13OhkMiveeiPFUnYxUarXgCRWHw6mJIATnKRl6KuGPxnAGbeSq8qCg4woGoFEA4vaBGtfUuY8IWK8suUaqXdX7Qsq7eicZTYeFUKYIIxd/gwFFYDdHmJebtqJsEbqbl5cv6xJW+66tgHZ1UngMmTbx7R09T9jIA4xwd+nDSxuRfysmeszr84Mao0tqoEYBi3nHAA3RsAuqjSxP2d9p/tbu1p2/Twwz0Bje+4sKpJABQx5liAmBJscmmVbSWnBTvG/4+uagQQiZk3gfGdsohlOgCSzwP4FTG2Mwn1Jg9iOYIJpwH0cTBdCOKBIeKSL6L5dia5tGR7Hw2rQgBqZI8EbyaguQRuGEAXE3UO5912sbd19XURGt4X6QfNEeC2EtoDwIdI0Lm5VamXS7P3z6oaBEBthvljAVzolBYGfiJAC3JWcotTW4Vvmzr3kyR5CYHPd27Pz9lW+jNKDc5t/bMIvADaY4lrmPlhZ5RwP0B3nnf2mHsXLVokndn+LXrRokXixV/vup1Y3gXQECe+mHF1Ppt6zImN39hAC8AwjNB+vOs3BJyhSwwD3UxsdGXStq6NDi5ixC8jYA2DhuvgBzASr3XvGTMuyLOJgRZAOGrOJMKjuoQz0EvgibaVflrXxgkuHJ17OUiuc/IuQozpuWxqpZN2/MQGWgBthvm8k2c/gW/MWenveklgJGrGQUjptkGgH+esZFDHLVyfMtXlpShu4pT4GVLQq7qze5KR6cqmphZ17AIgYszJOhiPYCrQB3Nrkq+70LTrLgJ7BwjHzDuJ8XWdjBl4SxbkWRvWdL6hgy8XE77qxn8k0bRVd7yAib6SzyT/o9x2vbAPrgAM81kCLtZKmnCvnUn9ixbWJVB7zLyfGQv13PEPbSv9WT2sv6hACuDwmP8+gFqK0cHMsjAkdNpTKzv+UAzr5v8nGnPGFph+T0SiqF9GT/fwnpFBnCMIpAAmGuZ5EtBadsXApryVuqRoJ3gACDu5S0n+pL06/TMPwijLZSAFEDHMWQC+r5UZ8x12Nv0NLazLoEg08VUQ/5ueW5ppW8nH9bD+oQIpgPao+XUm3KlDg2S+tCub/oEO1m2MGhwC6Cktv4S77ExqkRbWR1AwBWCYSxiYr8NDf0iM9fv5fySuyFXzTkWosF0nTjAesLOpL2phfQQFUgARw1wO4FodHkI9snXdus5uHazbmMtnzWod0tO6X8sv8XI7k75eC+sjKJgCiJqPgzBdh4dW7BliWVZBB+s2Ru08Gnbyzj4tv4wVdjY1QwvrIyiYAoiZD4Jxow4PvZAjKrUOT61P7G3q2acTJzEezGVTc3SwfmICKYBwLP5tYrpZh4iQlB9at7pzqw7WbcwVU+adHRIFrUUfRPhmLpO61e0YyvUXTAFEzS8R4R6d5Ih5Si6bXqODdRsTNuJRAlk6fon4tlwmfb8O1k9MMAUQS0wm5id0iGDixflM+hYdrNuYdgdfK2B8zs6m1rsdQ7n+AimANiNxlgDr3dYlXrNXp9SCEb+XXlHESLwG8Pt1OqEAccYGq+N3Olg/MYEUgJoCjhjmLgAn65BBTBfnssnndLBuYSZGzQmSsFHHn9pins+m3lMBkRYNL6gCQFvUXCUIsaIZDAA4Z1vpiXpYd1BtRiKvu2qYSa7MZzq1PmvdiU7fS2AFEDFMNRCkBoS0Lj+HhB0NAQ9GP9u2Uo9oJeIzKLACmDTp+hP7Wpp3EjBMhxMGftt0qPectWuXvaWDLxUTnrngH6in7+cQ+ICWD6YD3URjNlkdeiOGWk7dAwVWACrF9pj5KDNm6qZ7eFmY2pblyQuhWiL+01f+J+NgORgk5CNdVuds3Rz8xgVaAOEp5ngSUJs6tOM8/Fn4zx6IgMKxxHeIeYGDTmIhePz6VelfObDxFapNrK9RHdVYu2HmGFA1f/Qv4uWtvNcstg1M16HaLobWvjQRrtG1GcTROttKTnJm4y868AKIGHM/DhQ2O92VA+BnISlnlTtMHDbiH2bw953XI+B+EjQ+6PsDAy8A9XuIGOZiAI7n0tVGEUFYEgr137d2xUNqXEH7mjT9xnf39w1RC03ng9CkbXgYGNSx/2PzqAoBOH7zPjZLRg8L+aSQZPVT/6YN1nf3vl2HXmHcMCpETZeAyQDzlSAMddrxCq++SA5CfCKob/5H51QVAlABtxuJcxhqb3/xlcJFOo0Zcgcgfk+Dlb7Us3okA+8n4H1OXjjfth1GD0h8yrY6flmKePy2qRoBDIogfgODHiy7k7xjmYnoulwm+T3vmnDXc1UJQKUejiXuIOa73aXBNW9ftq3Uf7rmzQdHVSeAAREYibsI/DUf+NFvIqCrfoslUJUCGHgcRONzJdEDBISKJenl/xlQ6xEX5K2UqlZadVfVCkAx3RaNf1bVDyDQmAox/waRmJnLdGhNC1coxndstqoFoDIbLBQtUwK40k+CGXiiaUh/wun4gp8x6rRV9QI4kmQkak5kwreclJPRIeg4jORtMoRb3C5BU1IsLhjVjAAUF2qdfuspu6Yx85cBfNgFfv7qgsEvE8Q3WrF7VaX2IbiZzxFfNSWAowlSh0cwaJYEf76Md4Q3wLyGQ+LR/KrkC150QKV91qwAjiZ2YP1+SE6AxEdA8kwCj2WIUQwMVPwiYD9B7mXQDgK9KoGXhBCbcqs6flPpDvK6/boQgNckVrP/hgCqufdciL0hABdIrGYXDQFUc++5EHtDAC6QWM0ual4AaiXvC1t3jqV+caZA4SwpaAQkt6qjYwXRwLkAkvkAGPsgaPAvi20k6VV7zdIdHiwuDZReak4A7TMSI2U/PkOSLwHxRQyMI9AJpbDO4IMEbAXTcwD/kJrFj3KPJ7XqAZTSXiVsakIAk6bNf29B9s0oME8jpvFatftKYFvN/BHwC2aslFI+7ldl0hJC1TapWgEMlGc55Q2DWS3Vpkv9nhZWYmDQ0wT58ME/vWd1kEvCv5Maqk4AhmE07+dR00D0Vc8nfnR/RxLbIXhxK4Z1Wta3D+qaBQFXNQJQh0ccoNEmy8IdROK9QSDv2BgY+G8Adw/Hns5qmTCqCgEMrgjmDgDnBbHjjxcC/zIEzF1vpX8S9HgDLYCB/QB9fd9kydd79WLnVQepItYk6MEhPb23eb1juZwcAisAtTFUCJlhiA+Wk2DFbSVvgwhNDeo+gUAKIByNz1bHspT6/V7xTj/+mdAD4tttK13ewZceJBYoAQy+6L0rpVsk0gM+vHVJSLfynnlBekEMjAAmXHvt0GEHWlYANNnNXlAFmgThOSa8QhJbKSS3kZT7ZFPTn3cOL+w/fd8+6m4a3YoejKSmwgguiDNZYBwxzmbwxQCd4mY8AK3h/UNm5PNLDrnrtzRvgRDApcack5og1mofEVM8180grCgUQk9tWL3012WM51P7VPNslnw5QKrI07nFmy6OUIdcoLnpyvxjS94sjvYWUXEBqF/+CQeGbii785nfBCFNIrTcq6Vc7VPnfohl4ToGmQScWE7XSPBG2t8crvSdoKICGHjmY1S2zNv+XiYsbukduuTJJxcf3u1bTtcUt1UTTuiTCyTjZiIaWdzi7REEZIdhz7RKvhNUVAARB1XBj3uxHvzOXtbSF7r9iSeW7im1E8qxCxsLTibqvQdMqqRdSVwSkMxZqbnlxFGObUlBl9PgEVu1t4+JlpbiiyB/R6DZQRlpC8fMf6ICvqddOu6YpJkwJ59JqW3vvl8VEUDblMTHSMgXSvvOpzXNfS03+HW71+0RNWqJvkNpYqHK1Dm7VAUTxoX51alfODMsH+27ABRRovfQFqcjfGpoFRAL89mkqhcU2CscNRcS4T7njwR+tRuhc/0uK+O7AEp57g8Ue2JcE+RTuI9WZMRIzGDwcienjA/YM6XsbDLhp7p9FUB4auICFOTzTiZ2VOeDaXI+m8z7SUy5bbXF4hEh6UknFcbUXY5DoQu6VnVsLrd9XXvfBHD4k0/V+xuvG5z6TTDztflsWu8QSQeO/YC2R81pEvyYE8FLyC0nYt/5fn0a+iaASCwxD8z/5YR4Zrol6M/8Yvm0x+K3MpN6J9C/GKadTaX1DUpH+iKAwVKrh153spLncOHnqaWnFhzLiINj8A5H/cdW7PmAW6Vu34kJXwQQicVNMGnX0FHf+bK55ZwgjJW7ISN1vNyhpoNbCHS6rj+/xgY8F8Dg4Yq7tumeraNehEKETwdlkEe3w4rh2qOJi5j4We3PQ4nXuveMGef1amPPBaBehJiwohhBf/0/4SE7k/qCNr6KgE6OxB1IizDVzqQyXqbouQDCRryLQFdoJrG3pT90ZqXG9jVjLBmm5g6A3m0EGqHpJG9bKWel8jUdH4F5KgC1Y6ev0L9De9MG0512NvnvDnOoKngkZi4C41/1guZ+IPQ+2+rYqYd3jvJUABEjcRvA92qFxfwmNYvTam3v3bG5q4rkAk3b9dcT8ELbSn9Li8MSQB4LwFQjWpqraPg+20p/qYQcqs6kPWbez4yFOoEz6MW8lbxAB1sKxjMBDH769OzWvf1X8hDoUogrx0adQkKgV3R8qD2ILX1DR3s1++mZACYa5ucloHuo809tK3W+DiG1ggkb5hYCPqGTD4GuzFnJtTpYpxjPBBCOJR7QPWGrFoZ8nRJ/eNpY6zRxLw/I9kwAESP+c92JHxL4aNAPV3LawcXwalGMEKx1nJyaIOqyOjXfpYq1/Lf/90QAqizLi6/sfEvv1E/+X9tKq2rfnhz26IwOX9H6B2QzHbCzSbUK2XWOPBFA5Kp5pyJU2K5FJ8GyMynNQ6K1PFYNKGLMyeqeQtofEmOfWtnxB7eT80QA7THzCmZ06QSryq4LJv2hYh2nVYIpEM/ULXNPRJflMsln3E7NEwGEDXM+AUvcDrae/RHzvFw2rWokuHp5IoB2I/41Bt3laqT17syjYXJPBBCOJu4j4lvrvc/czJ+Z78ln07e76VP58kQAkWgiCWLT7WDr2h+hw86k5rnNgUcCiD8CoqvdDrae/UnIR7qsztluc+CNAGLxZWC6zu1g69kfsVyWy3be4DYHngigPRpfwEQPuB1sPfsj5pty2bTrX1aeCGDgFG40vQ7gpHruNBdz/0sBfaf/vVPPy2nHEwGogMJGPArQSt3p4HKSqGXbwZNJeVreSme9yNMzAahgI0b8MmDgUTDOi+DrwOdWgG+yrfTTXuXqqQAOB01tUxIfFSE+gyQJrxKpJb8sWMoC/bZrdfIlLyaAjubKDwHUUt/UXC4NAdRclzpLqCEAZ3zVHLohgJrrUmcJNQTgjK+aQzcEUHNd6iyhhgCc8VVz6IYAaq5LnSXUEIAzvmoO3RBAzXWps4T+D/ke0cxe6VbjAAAAAElFTkSuQmCC" preserveAspectRatio="none"/><rect x="0" y="79" width="146" height="57" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 144px; height: 1px; padding-top: 108px; margin-left: 1px;"><div data-drawio-colors="color: #FFFFFF; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(255, 255, 255); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><font color="#0a0a0a">Data Scientists</font></div></div></div></foreignObject><text x="73" y="114" fill="#FFFFFF" font-family="Helvetica" font-size="22px" text-anchor="middle">Data Scientis...</text></switch></g><ellipse cx="651" cy="53" rx="52" ry="52" fill="#cce5ff" stroke="#36393d" stroke-width="3" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 102px; height: 1px; padding-top: 53px; margin-left: 600px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">Deliver</div></div></div></foreignObject><text x="651" y="60" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">Deliver</text></switch></g><path d="M 313 53 Q 313 53 368.9 53" fill="none" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" stroke-dasharray="9 9" pointer-events="stroke"/><path d="M 375.65 53 L 366.65 57.5 L 368.9 53 L 366.65 48.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/><rect x="183" y="23" width="130" height="60" fill="#cdeb8b" stroke="#36393d" stroke-width="3" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 53px; margin-left: 184px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">write build.envd</div></div></div></foreignObject><text x="248" y="60" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">write build....</text></switch></g><path d="M 509 53 Q 509 53 588.9 53" fill="none" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" stroke-dasharray="9 9" pointer-events="stroke"/><path d="M 595.65 53 L 586.65 57.5 L 588.9 53 L 586.65 48.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/><rect x="379" y="23" width="130" height="60" fill="#cdeb8b" stroke="#36393d" stroke-width="3" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 53px; margin-left: 380px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">envd up and develop</div></div></div></foreignObject><text x="444" y="60" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">envd up and...</text></switch></g><rect x="80" y="189" width="130" height="60" fill="#bac8d3" stroke="#23445d" stroke-width="3" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 219px; margin-left: 81px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"><div>git clone</div></div></div></div></foreignObject><text x="145" y="226" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">git clone</text></switch></g><rect x="240" y="159" width="130" height="60" fill="#bac8d3" stroke="#23445d" stroke-width="3" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 189px; margin-left: 241px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">configure tools</div></div></div></foreignObject><text x="305" y="196" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">configure to...</text></switch></g><rect x="134" y="209" width="180" height="64" fill="#bac8d3" stroke="#23445d" stroke-width="3" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 178px; height: 1px; padding-top: 241px; margin-left: 135px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">build and install system deps</div></div></div></foreignObject><text x="224" y="248" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">build and instal...</text></switch></g><rect x="360" y="169" width="130" height="60" fill="#bac8d3" stroke="#23445d" stroke-width="3" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 199px; margin-left: 361px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">update python deps</div></div></div></foreignObject><text x="425" y="206" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">update pytho...</text></switch></g><rect x="314" y="213" width="130" height="60" fill="#bac8d3" stroke="#23445d" stroke-width="3" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 243px; margin-left: 315px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">debug datasets</div></div></div></foreignObject><text x="379" y="250" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">debug datase...</text></switch></g><rect x="230" y="249" width="170" height="60" fill="#bac8d3" stroke="#23445d" stroke-width="3" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 168px; height: 1px; padding-top: 279px; margin-left: 231px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">configure model storage</div></div></div></foreignObject><text x="315" y="286" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">configure model...</text></switch></g><rect x="420" y="189" width="130" height="60" fill="#bac8d3" stroke="#23445d" stroke-width="3" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 219px; margin-left: 421px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">trial and error</div></div></div></foreignObject><text x="485" y="226" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">trial and er...</text></switch></g><rect x="444" y="149" width="130" height="60" fill="#bac8d3" stroke="#23445d" stroke-width="3" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 179px; margin-left: 445px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">git push</div></div></div></foreignObject><text x="509" y="186" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">git push</text></switch></g><rect x="470" y="229" width="130" height="60" fill="#bac8d3" stroke="#23445d" stroke-width="3" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 259px; margin-left: 471px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"><div>git clone</div></div></div></div></foreignObject><text x="535" y="266" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">git clone</text></switch></g><rect x="480" y="189" width="130" height="60" fill="#bac8d3" stroke="#23445d" stroke-width="3" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 219px; margin-left: 481px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">write dockefiles</div></div></div></foreignObject><text x="545" y="226" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">write dockef...</text></switch></g><rect x="200" y="279" width="130" height="60" fill="#bac8d3" stroke="#23445d" stroke-width="3" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 309px; margin-left: 201px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">pipeline the data</div></div></div></foreignObject><text x="265" y="316" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">pipeline the...</text></switch></g><rect x="460" y="159" width="130" height="60" fill="#bac8d3" stroke="#23445d" stroke-width="3" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 189px; margin-left: 461px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">training</div></div></div></foreignObject><text x="525" y="196" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">training</text></switch></g><rect x="370" y="259" width="130" height="60" fill="#bac8d3" stroke="#23445d" stroke-width="3" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 289px; margin-left: 371px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">save model</div></div></div></foreignObject><text x="435" y="296" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">save model</text></switch></g><image x="262.5" y="118.5" width="232" height="232" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAAAXNSR0IArs4c6QAABYNJREFUeF7t3btu1EAYhuEvVdKkgSaXAffAKWUkuM4gKNJwByDlNlIhyiRQoBFrsXFsf2N7Dv8pFRL2rD3v4/FmSfAJ4sv1DJy4Pvs4eQQA5wiWAFwCuANw63yOtJ/+awAXAG6mTmQOwAcA1wDuAbwD8F37LDg9/lcAvgE4B/AJwJfxPEwBGOKfHTb+FQhU8hnivzwc/eMUgjGAtOynK/90dMo/AbyJ24EaCGnZT1f+i9ERPwC4Or4djAGM1RzvHyuBjv6s4VsAP4ZTmboFsAHiPYFcCKzdk/jpNObeBLKBAoE8BKzZs/hLANLfsQEDgRwErNVkfAYgEMgJvHQkm+PnAAgEshHsip8LIBDIRLA7/hoAgUAWgiLx1wIIBDIQFIu/BUAg6IugaPytAAJBHwTF4+8BEAjaIqgSfy+AQNAGQbX4JQAEgroIqsYvBSAQ1EFQPX5JAIGgLIIm8UsDCARlEDSLXwNAINiHoGn8WgACwTYEzePXBBAI1iHoEr82gECQh6Bb/BYAAsEygq7xWwEIBNMIusdvCSAQPEUgIn5rAIHgHwIx8XsAyJkAyz9tLCp+LwBeEYiL3xOANwQi4/cG4AWB2PgSAFhHIDq+FABWEYiPLwmANQQq4ksDYAWBmvgSAWhHoCq+VABaEaiLLxmANgQq40sHoAWB2vgaAEhHoDq+FgBSEaiPrwmANAQm4msDIAWBmfgaAfRGYCq+VgC9EJiLrxlAawQm42sH0AqB2fgWANRGYDq+FQC1EJiPbwlAaQQu4lsDUAqBm/gWAexF4Cq+VQBbEbiLbxnAWgQu41sHkIvg9+EBS8PTtdJ+w1d6TtLswxaON9T6Zw9PDp17glZqlp6Glr7GT9ca/s78k9I8AGArwdTFa/7KH07aC4A1CNzE9/AeYHx1L73ZS9u6ih8Ani/+7h6OGbcA5wi8AGBL/5iBm5XAA4D4NnDhQwrrAHI+4ftDPgiy/H8WzT47WOsHW8fHnRN/eIo229YsAqsrAAs69fEu28ckAosAWMilz/bZvuYQWAPAAub8ww4bwxQCSwBYuJz4w3sINpYZBFYAsGBr4rtCYAFAjfhuEGgHUDO+CwSaAbSIbx6BVgAt45tGoBFAj/hmEWgD0DO+SQSaAEiIbw6BFgCS4ptCoAGAxPhmEEgHIDm+CQSSAWiIrx6BVACa4qtGIBGAxvhqEUgDoDm+SgSSAFiIrw6BFACW4qtCIAGAxfhqEPQGYDm+CgQ9AXiILx5BLwCe4otG0AOAx/hiEbQG4Dm+SAQtAUT8/7+5yOai2e8dtALATnjLz+1r/wVWNidNELQAwE7UY3wxt4PaACI+X6fYHFVdCWoCYCfm+cofs2BzVQ1BLQDshCL+85WBzVkVBDUAsBOJ+PO3BTZ3xRGUBsBOIOILe09QEkDE53Fzt2BzWWwlKAWAHXBc+bnpG39YVAJAxF8fN3cPNre7V4K9ANgBxpWfm7rTG8M9ACL+/ri5I7C53rwSbAXADiiu/Ny0+duxOd+EYAsAdiARPz/q2i3Z3K9GsBYAO4CIvzbp+u1Zg1UI1gBgLxzx18fcugdrkY0gFwB7wYi/NeX2/ViTLAQ5ANgLRfztEffuydpQBAwAe4GIvzfh/v1Zo0UESwDYwBF/f7xSI7BWswjmALABI36pdOXGYc0mEUwBYANF/HLRSo/E2j1DMAbABoj4pZOVH481fIJgDOASwDWA09FxpYcsm3+QcvkW3Uace1LaA4ArADfDkU3dAt4D+Azg7LCRu8epdstW9oXHK8EjgI8Avh6/zNybwAHBPYBY9suGaTnagOB8Kn46kKVvA9Pt4A7AbcsjjtcqPgPpdnBxvOznrADFjyIGlDkD7JNAmUcdR1VsBgJAsanUOdBf59G+n5kqRBoAAAAASUVORK5CYII=" preserveAspectRatio="none" pointer-events="none"/></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Text is not SVG - cannot display</text></a></switch></svg> \ No newline at end of file diff --git a/docs/images/before.svg b/docs/images/before.svg deleted file mode 100644 index 622b0e3a5..000000000 --- a/docs/images/before.svg +++ /dev/null @@ -1,3 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="892px" height="498px" viewBox="-0.5 -0.5 892 498"><defs/><g><path d="M 108 207 Q 150 207 150 224 Q 150 241 165 241 Q 180 241 180 266 Q 180 291 217.5 291 Q 255 291 255 247.1" fill="none" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" stroke-dasharray="9 9" pointer-events="stroke"/><path d="M 255 240.35 L 259.5 249.35 L 255 247.1 L 250.5 249.35 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/><image x="39.5" y="172.5" width="68" height="68" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAAAXNSR0IArs4c6QAAEPpJREFUeF7tXWuUXFWV/vapdHdIhyGJAaNrDIgCUdRlBAEZwbDk0VUdQyR1Ky8SnqZu5YEwAQdRnOAM4/BQI5l0VTWYoDyS1K0EVpK61QE0QWQJxvhYgIYomBWdIRnzUEg6ne6us2ed7sSJCVLnVt1769bj/ukf/e199v7OV/dxHvsQGlddM0B1nX0jeTQEUOciaAigIYA6Z6DO02/cARoCqHMG6jz9xh2gIYA6Z6DO02/cARoCqC8GPjd9zmjZHzoVhJEsZavKnoQ4QFTYSwI71q3o3F1PjNT0HWDChEVDho/e+ekC0SUATyDgIwBGFengvQy8TISNzLTx4J/e/fymTYv6a1UUNSmAtimJjwnB1wM8HaBTyuk8ZuyC4BXEvMy2Ol8qx1cQbWtKABON+Kck8BWAIurO7jLhDNB6yMLd9urOF132XTF3bpNUkUQixtwxEv33CoirPej4t8mJ13OhkMiveeiPFUnYxUarXgCRWHw6mJIATnKRl6KuGPxnAGbeSq8qCg4woGoFEA4vaBGtfUuY8IWK8suUaqXdX7Qsq7eicZTYeFUKYIIxd/gwFFYDdHmJebtqJsEbqbl5cv6xJW+66tgHZ1UngMmTbx7R09T9jIA4xwd+nDSxuRfysmeszr84Mao0tqoEYBi3nHAA3RsAuqjSxP2d9p/tbu1p2/Twwz0Bje+4sKpJABQx5liAmBJscmmVbSWnBTvG/4+uagQQiZk3gfGdsohlOgCSzwP4FTG2Mwn1Jg9iOYIJpwH0cTBdCOKBIeKSL6L5dia5tGR7Hw2rQgBqZI8EbyaguQRuGEAXE3UO5912sbd19XURGt4X6QfNEeC2EtoDwIdI0Lm5VamXS7P3z6oaBEBthvljAVzolBYGfiJAC3JWcotTW4Vvmzr3kyR5CYHPd27Pz9lW+jNKDc5t/bMIvADaY4lrmPlhZ5RwP0B3nnf2mHsXLVokndn+LXrRokXixV/vup1Y3gXQECe+mHF1Ppt6zImN39hAC8AwjNB+vOs3BJyhSwwD3UxsdGXStq6NDi5ixC8jYA2DhuvgBzASr3XvGTMuyLOJgRZAOGrOJMKjuoQz0EvgibaVflrXxgkuHJ17OUiuc/IuQozpuWxqpZN2/MQGWgBthvm8k2c/gW/MWenveklgJGrGQUjptkGgH+esZFDHLVyfMtXlpShu4pT4GVLQq7qze5KR6cqmphZ17AIgYszJOhiPYCrQB3Nrkq+70LTrLgJ7BwjHzDuJ8XWdjBl4SxbkWRvWdL6hgy8XE77qxn8k0bRVd7yAib6SzyT/o9x2vbAPrgAM81kCLtZKmnCvnUn9ixbWJVB7zLyfGQv13PEPbSv9WT2sv6hACuDwmP8+gFqK0cHMsjAkdNpTKzv+UAzr5v8nGnPGFph+T0SiqF9GT/fwnpFBnCMIpAAmGuZ5EtBadsXApryVuqRoJ3gACDu5S0n+pL06/TMPwijLZSAFEDHMWQC+r5UZ8x12Nv0NLazLoEg08VUQ/5ueW5ppW8nH9bD+oQIpgPao+XUm3KlDg2S+tCub/oEO1m2MGhwC6Cktv4S77ExqkRbWR1AwBWCYSxiYr8NDf0iM9fv5fySuyFXzTkWosF0nTjAesLOpL2phfQQFUgARw1wO4FodHkI9snXdus5uHazbmMtnzWod0tO6X8sv8XI7k75eC+sjKJgCiJqPgzBdh4dW7BliWVZBB+s2Ru08Gnbyzj4tv4wVdjY1QwvrIyiYAoiZD4Jxow4PvZAjKrUOT61P7G3q2acTJzEezGVTc3SwfmICKYBwLP5tYrpZh4iQlB9at7pzqw7WbcwVU+adHRIFrUUfRPhmLpO61e0YyvUXTAFEzS8R4R6d5Ih5Si6bXqODdRsTNuJRAlk6fon4tlwmfb8O1k9MMAUQS0wm5id0iGDixflM+hYdrNuYdgdfK2B8zs6m1rsdQ7n+AimANiNxlgDr3dYlXrNXp9SCEb+XXlHESLwG8Pt1OqEAccYGq+N3Olg/MYEUgJoCjhjmLgAn65BBTBfnssnndLBuYSZGzQmSsFHHn9pins+m3lMBkRYNL6gCQFvUXCUIsaIZDAA4Z1vpiXpYd1BtRiKvu2qYSa7MZzq1PmvdiU7fS2AFEDFMNRCkBoS0Lj+HhB0NAQ9GP9u2Uo9oJeIzKLACmDTp+hP7Wpp3EjBMhxMGftt0qPectWuXvaWDLxUTnrngH6in7+cQ+ICWD6YD3URjNlkdeiOGWk7dAwVWACrF9pj5KDNm6qZ7eFmY2pblyQuhWiL+01f+J+NgORgk5CNdVuds3Rz8xgVaAOEp5ngSUJs6tOM8/Fn4zx6IgMKxxHeIeYGDTmIhePz6VelfObDxFapNrK9RHdVYu2HmGFA1f/Qv4uWtvNcstg1M16HaLobWvjQRrtG1GcTROttKTnJm4y868AKIGHM/DhQ2O92VA+BnISlnlTtMHDbiH2bw953XI+B+EjQ+6PsDAy8A9XuIGOZiAI7n0tVGEUFYEgr137d2xUNqXEH7mjT9xnf39w1RC03ng9CkbXgYGNSx/2PzqAoBOH7zPjZLRg8L+aSQZPVT/6YN1nf3vl2HXmHcMCpETZeAyQDzlSAMddrxCq++SA5CfCKob/5H51QVAlABtxuJcxhqb3/xlcJFOo0Zcgcgfk+Dlb7Us3okA+8n4H1OXjjfth1GD0h8yrY6flmKePy2qRoBDIogfgODHiy7k7xjmYnoulwm+T3vmnDXc1UJQKUejiXuIOa73aXBNW9ftq3Uf7rmzQdHVSeAAREYibsI/DUf+NFvIqCrfoslUJUCGHgcRONzJdEDBISKJenl/xlQ6xEX5K2UqlZadVfVCkAx3RaNf1bVDyDQmAox/waRmJnLdGhNC1coxndstqoFoDIbLBQtUwK40k+CGXiiaUh/wun4gp8x6rRV9QI4kmQkak5kwreclJPRIeg4jORtMoRb3C5BU1IsLhjVjAAUF2qdfuspu6Yx85cBfNgFfv7qgsEvE8Q3WrF7VaX2IbiZzxFfNSWAowlSh0cwaJYEf76Md4Q3wLyGQ+LR/KrkC150QKV91qwAjiZ2YP1+SE6AxEdA8kwCj2WIUQwMVPwiYD9B7mXQDgK9KoGXhBCbcqs6flPpDvK6/boQgNckVrP/hgCqufdciL0hABdIrGYXDQFUc++5EHtDAC6QWM0ual4AaiXvC1t3jqV+caZA4SwpaAQkt6qjYwXRwLkAkvkAGPsgaPAvi20k6VV7zdIdHiwuDZReak4A7TMSI2U/PkOSLwHxRQyMI9AJpbDO4IMEbAXTcwD/kJrFj3KPJ7XqAZTSXiVsakIAk6bNf29B9s0oME8jpvFatftKYFvN/BHwC2aslFI+7ldl0hJC1TapWgEMlGc55Q2DWS3Vpkv9nhZWYmDQ0wT58ME/vWd1kEvCv5Maqk4AhmE07+dR00D0Vc8nfnR/RxLbIXhxK4Z1Wta3D+qaBQFXNQJQh0ccoNEmy8IdROK9QSDv2BgY+G8Adw/Hns5qmTCqCgEMrgjmDgDnBbHjjxcC/zIEzF1vpX8S9HgDLYCB/QB9fd9kydd79WLnVQepItYk6MEhPb23eb1juZwcAisAtTFUCJlhiA+Wk2DFbSVvgwhNDeo+gUAKIByNz1bHspT6/V7xTj/+mdAD4tttK13ewZceJBYoAQy+6L0rpVsk0gM+vHVJSLfynnlBekEMjAAmXHvt0GEHWlYANNnNXlAFmgThOSa8QhJbKSS3kZT7ZFPTn3cOL+w/fd8+6m4a3YoejKSmwgguiDNZYBwxzmbwxQCd4mY8AK3h/UNm5PNLDrnrtzRvgRDApcack5og1mofEVM8180grCgUQk9tWL3012WM51P7VPNslnw5QKrI07nFmy6OUIdcoLnpyvxjS94sjvYWUXEBqF/+CQeGbii785nfBCFNIrTcq6Vc7VPnfohl4ToGmQScWE7XSPBG2t8crvSdoKICGHjmY1S2zNv+XiYsbukduuTJJxcf3u1bTtcUt1UTTuiTCyTjZiIaWdzi7REEZIdhz7RKvhNUVAARB1XBj3uxHvzOXtbSF7r9iSeW7im1E8qxCxsLTibqvQdMqqRdSVwSkMxZqbnlxFGObUlBl9PgEVu1t4+JlpbiiyB/R6DZQRlpC8fMf6ICvqddOu6YpJkwJ59JqW3vvl8VEUDblMTHSMgXSvvOpzXNfS03+HW71+0RNWqJvkNpYqHK1Dm7VAUTxoX51alfODMsH+27ABRRovfQFqcjfGpoFRAL89mkqhcU2CscNRcS4T7njwR+tRuhc/0uK+O7AEp57g8Ue2JcE+RTuI9WZMRIzGDwcienjA/YM6XsbDLhp7p9FUB4auICFOTzTiZ2VOeDaXI+m8z7SUy5bbXF4hEh6UknFcbUXY5DoQu6VnVsLrd9XXvfBHD4k0/V+xuvG5z6TTDztflsWu8QSQeO/YC2R81pEvyYE8FLyC0nYt/5fn0a+iaASCwxD8z/5YR4Zrol6M/8Yvm0x+K3MpN6J9C/GKadTaX1DUpH+iKAwVKrh153spLncOHnqaWnFhzLiINj8A5H/cdW7PmAW6Vu34kJXwQQicVNMGnX0FHf+bK55ZwgjJW7ISN1vNyhpoNbCHS6rj+/xgY8F8Dg4Yq7tumeraNehEKETwdlkEe3w4rh2qOJi5j4We3PQ4nXuveMGef1amPPBaBehJiwohhBf/0/4SE7k/qCNr6KgE6OxB1IizDVzqQyXqbouQDCRryLQFdoJrG3pT90ZqXG9jVjLBmm5g6A3m0EGqHpJG9bKWel8jUdH4F5KgC1Y6ev0L9De9MG0512NvnvDnOoKngkZi4C41/1guZ+IPQ+2+rYqYd3jvJUABEjcRvA92qFxfwmNYvTam3v3bG5q4rkAk3b9dcT8ELbSn9Li8MSQB4LwFQjWpqraPg+20p/qYQcqs6kPWbez4yFOoEz6MW8lbxAB1sKxjMBDH769OzWvf1X8hDoUogrx0adQkKgV3R8qD2ILX1DR3s1++mZACYa5ucloHuo809tK3W+DiG1ggkb5hYCPqGTD4GuzFnJtTpYpxjPBBCOJR7QPWGrFoZ8nRJ/eNpY6zRxLw/I9kwAESP+c92JHxL4aNAPV3LawcXwalGMEKx1nJyaIOqyOjXfpYq1/Lf/90QAqizLi6/sfEvv1E/+X9tKq2rfnhz26IwOX9H6B2QzHbCzSbUK2XWOPBFA5Kp5pyJU2K5FJ8GyMynNQ6K1PFYNKGLMyeqeQtofEmOfWtnxB7eT80QA7THzCmZ06QSryq4LJv2hYh2nVYIpEM/ULXNPRJflMsln3E7NEwGEDXM+AUvcDrae/RHzvFw2rWokuHp5IoB2I/41Bt3laqT17syjYXJPBBCOJu4j4lvrvc/czJ+Z78ln07e76VP58kQAkWgiCWLT7WDr2h+hw86k5rnNgUcCiD8CoqvdDrae/UnIR7qsztluc+CNAGLxZWC6zu1g69kfsVyWy3be4DYHngigPRpfwEQPuB1sPfsj5pty2bTrX1aeCGDgFG40vQ7gpHruNBdz/0sBfaf/vVPPy2nHEwGogMJGPArQSt3p4HKSqGXbwZNJeVreSme9yNMzAahgI0b8MmDgUTDOi+DrwOdWgG+yrfTTXuXqqQAOB01tUxIfFSE+gyQJrxKpJb8sWMoC/bZrdfIlLyaAjubKDwHUUt/UXC4NAdRclzpLqCEAZ3zVHLohgJrrUmcJNQTgjK+aQzcEUHNd6iyhhgCc8VVz6IYAaq5LnSXUEIAzvmoO3RBAzXWps4T+D/ke0cxe6VbjAAAAAElFTkSuQmCC" preserveAspectRatio="none"/><path d="M 255 177 Q 255 149 311.25 149 Q 367.5 149 367.5 131.1" fill="none" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" stroke-dasharray="9 9" pointer-events="stroke"/><path d="M 367.5 124.35 L 372 133.35 L 367.5 131.1 L 363 133.35 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/><path d="M 400 106 Q 450 106 450 148.5 Q 450 191 415 191 Q 380 191 380 210.9" fill="none" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" stroke-dasharray="9 9" pointer-events="stroke"/><path d="M 380 217.65 L 375.5 208.65 L 380 210.9 L 384.5 208.65 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/><path d="M 450 221 Q 440 221 450 221 Q 460 221 460 176 Q 460 131 512.5 131 Q 565 131 565 71.1" fill="none" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" stroke-dasharray="9 9" pointer-events="stroke"/><path d="M 565 64.35 L 569.5 73.35 L 565 71.1 L 560.5 73.35 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/><rect x="360" y="221" width="180" height="70" fill="#bac8d3" stroke="#23445d" stroke-width="3" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 178px; height: 1px; padding-top: 256px; margin-left: 361px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">build and install system deps</div></div></div></foreignObject><text x="450" y="263" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">build and instal...</text></switch></g><path d="M 565 61 Q 565 96 585 96 Q 605 96 605 120.9" fill="none" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" stroke-dasharray="9 9" pointer-events="stroke"/><path d="M 605 127.65 L 600.5 118.65 L 605 120.9 L 609.5 118.65 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/><rect x="500" y="1" width="130" height="60" fill="#bac8d3" stroke="#23445d" stroke-width="3" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 31px; margin-left: 501px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">update python deps</div></div></div></foreignObject><text x="565" y="38" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">update pytho...</text></switch></g><path d="M 605 191 Q 605 221 607.55 221 Q 610.1 221 610.07 238.38" fill="none" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" stroke-dasharray="9 9" pointer-events="stroke"/><path d="M 610.06 245.13 L 605.58 236.12 L 610.07 238.38 L 614.58 236.13 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/><rect x="540" y="131" width="130" height="60" fill="#bac8d3" stroke="#23445d" stroke-width="3" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 161px; margin-left: 541px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">debug datasets</div></div></div></foreignObject><text x="605" y="168" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">debug datase...</text></switch></g><path d="M 632.5 311 Q 632.5 343.5 655 343.5 Q 677.5 343.5 677.5 365.9" fill="none" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" stroke-dasharray="9 9" pointer-events="stroke"/><path d="M 677.5 372.65 L 673 363.65 L 677.5 365.9 L 682 363.65 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/><path d="M 320 363 Q 355 363 355 354.5 Q 355 346 379.9 346" fill="none" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" stroke-dasharray="9 9" pointer-events="stroke"/><path d="M 386.65 346 L 377.65 350.5 L 379.9 346 L 377.65 341.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/><path d="M 230 308 Q 230 31 489.9 31" fill="none" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" stroke-dasharray="9 9" pointer-events="stroke"/><path d="M 496.65 31 L 487.65 35.5 L 489.9 31 L 487.65 26.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/><rect x="140" y="308" width="180" height="110" fill="#bac8d3" stroke="#23445d" stroke-width="3" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 178px; height: 1px; padding-top: 363px; margin-left: 141px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">trial and error</div></div></div></foreignObject><text x="230" y="370" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">trial and error</text></switch></g><rect x="0" y="251" width="146" height="57" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 144px; height: 1px; padding-top: 280px; margin-left: 1px;"><div data-drawio-colors="color: #FFFFFF; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(255, 255, 255); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><font color="#0a0a0a">Data Scientists</font></div></div></div></foreignObject><text x="73" y="286" fill="#FFFFFF" font-family="Helvetica" font-size="22px" text-anchor="middle">Data Scientis...</text></switch></g><path d="M 520 346 Q 550 346 550 376 Q 550 406 569.9 406" fill="none" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" stroke-dasharray="9 9" pointer-events="stroke"/><path d="M 576.65 406 L 567.65 410.5 L 569.9 406 L 567.65 401.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/><rect x="390" y="316" width="130" height="60" fill="#bac8d3" stroke="#23445d" stroke-width="3" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 346px; margin-left: 391px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">write dockefiles</div></div></div></foreignObject><text x="455" y="353" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">write dockef...</text></switch></g><path d="M 580 406 Q 530 406 530 436 Q 530 466 490.1 466" fill="none" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" stroke-dasharray="9 9" pointer-events="stroke"/><path d="M 483.35 466 L 492.35 461.5 L 490.1 466 L 492.35 470.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/><path d="M 480 481 Q 825 481 825 401.1" fill="none" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" stroke-dasharray="9 9" pointer-events="stroke"/><path d="M 825 394.35 L 829.5 403.35 L 825 401.1 L 820.5 403.35 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/><path d="M 350 451 Q 230 451 230 428.1" fill="none" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" stroke-dasharray="9 9" pointer-events="stroke"/><path d="M 230 421.35 L 234.5 430.35 L 230 428.1 L 225.5 430.35 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/><rect x="350" y="436" width="130" height="60" fill="#bac8d3" stroke="#23445d" stroke-width="3" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 466px; margin-left: 351px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">training</div></div></div></foreignObject><text x="415" y="473" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">training</text></switch></g><path d="M 825 331 Q 825 311 825 313 Q 825 315 825 305.1" fill="none" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" stroke-dasharray="9 9" pointer-events="stroke"/><path d="M 825 298.35 L 829.5 307.35 L 825 305.1 L 820.5 307.35 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/><rect x="760" y="331" width="130" height="60" fill="#bac8d3" stroke="#23445d" stroke-width="3" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 361px; margin-left: 761px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">save model</div></div></div></foreignObject><text x="825" y="368" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">save model</text></switch></g><ellipse cx="825" cy="243" rx="52" ry="52" fill="#cce5ff" stroke="#36393d" stroke-width="3" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 102px; height: 1px; padding-top: 243px; margin-left: 774px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">Deliver</div></div></div></foreignObject><text x="825" y="250" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">Deliver</text></switch></g><rect x="190" y="177" width="130" height="60" fill="#bac8d3" stroke="#23445d" stroke-width="3" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 207px; margin-left: 191px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><div>git clone</div></div></div></div></foreignObject><text x="255" y="214" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">git clone</text></switch></g><rect x="270" y="61" width="130" height="60" fill="#bac8d3" stroke="#23445d" stroke-width="3" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 91px; margin-left: 271px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">configure tools</div></div></div></foreignObject><text x="335" y="98" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">configure to...</text></switch></g><path d="M 710 406 Q 730 406 730 344.75 Q 730 283.5 683.75 283.5 Q 637.5 283.5 637.5 201.1" fill="none" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" stroke-dasharray="9 9" pointer-events="stroke"/><path d="M 637.5 194.35 L 642 203.35 L 637.5 201.1 L 633 203.35 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/><rect x="580" y="376" width="130" height="60" fill="#bac8d3" stroke="#23445d" stroke-width="3" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 406px; margin-left: 581px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">pipeline the data</div></div></div></foreignObject><text x="645" y="413" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">pipeline the...</text></switch></g><rect x="590" y="251" width="170" height="60" fill="#bac8d3" stroke="#23445d" stroke-width="3" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 168px; height: 1px; padding-top: 281px; margin-left: 591px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 22px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">configure model storage</div></div></div></foreignObject><text x="675" y="288" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="22px" text-anchor="middle">configure model...</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Text is not SVG - cannot display</text></a></switch></svg> \ No newline at end of file diff --git a/docs/images/envd.png b/docs/images/envd.png deleted file mode 100644 index b61c87462..000000000 Binary files a/docs/images/envd.png and /dev/null differ diff --git a/docs/proposals/20220826-image-runtime-redistributed-metadata.md b/docs/proposals/20220826-image-runtime-redistributed-metadata.md index 7df46dc59..8d8d2c30d 100644 --- a/docs/proposals/20220826-image-runtime-redistributed-metadata.md +++ b/docs/proposals/20220826-image-runtime-redistributed-metadata.md @@ -4,15 +4,15 @@ Authors: ## Summary -The `envd` would add (runing graph metadata)[https://github.com/tensorchord/envd/blob/630ada172bdf876c3b749329fdbe284c108051f2/pkg/lang/ir/types.go#L70] would be encoded into a ASCII string and added to be image(OCI Spec) as a config. +The `envd` would add (running graph metadata)[https://github.com/tensorchord/envd/blob/630ada172bdf876c3b749329fdbe284c108051f2/pkg/lang/ir/types.go#L70] would be encoded into a ASCII string and added to be image(OCI Spec) as a config. we named the above mentioned encoded config `Envd Runtime Graph Label` ## Motivation -This proposal is part of effort to define what the artifact `envd` delivery and decoupling the phases of build and runing. It will be friendly for running a envd environment even at the absence of `build.env`. +This proposal is part of effort to define what the artifact `envd` delivery and decoupling the phases of build and running. It will be friendly for running a envd environment even at the absence of `build.env`. -the concept of `running context` is a also needed as addition of `build contxt` for example: +the concept of `running context` is a also needed as addition of `build context` for example: - An engineer build a easy-to-use env for his/her interns for the quick use of company tools. - Kubernetes remote runtime support in the future. @@ -26,12 +26,12 @@ the concept of `running context` is a also needed as addition of `build contxt` ## Implementations There are two parts of runtime configuration that can be used in envd. -- OCI sepecifications specific: +- OCI specifications specific: - ExposedPort - Entrypoint - Env - Cmd -- Custome Labels +- Custom Labels For some parts of runtime configuration, we could use the OCI part such as environment variables. We still need to deal with extra parts such as port bindings which not covered by the OCI spec. @@ -51,4 +51,4 @@ we use the following labels: - ai.tensorchord.envd.runtimeGraph.version - ai.tensorchord.envd.runtimeGraph.Daemon -- ai.tensorchord.envd.runtimeGraph.Expose \ No newline at end of file +- ai.tensorchord.envd.runtimeGraph.Expose diff --git a/e2e/v1/cli/build_test.go b/e2e/cli/build_test.go similarity index 97% rename from e2e/v1/cli/build_test.go rename to e2e/cli/build_test.go index 5044165f2..41d2f28d5 100644 --- a/e2e/v1/cli/build_test.go +++ b/e2e/cli/build_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - e2e "github.com/tensorchord/envd/e2e/v1" + "github.com/tensorchord/envd/e2e" "github.com/tensorchord/envd/pkg/app" "github.com/tensorchord/envd/pkg/driver/docker" "github.com/tensorchord/envd/pkg/envd" diff --git a/e2e/v0/cli/bytecode_hash_test.go b/e2e/cli/bytecode_hash_test.go similarity index 95% rename from e2e/v0/cli/bytecode_hash_test.go rename to e2e/cli/bytecode_hash_test.go index 4e09c5084..d980c716c 100644 --- a/e2e/v0/cli/bytecode_hash_test.go +++ b/e2e/cli/bytecode_hash_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - e2e "github.com/tensorchord/envd/e2e/v0" + "github.com/tensorchord/envd/e2e" ) func appendSomeToFile(path string) { diff --git a/e2e/v1/cli/context_test.go b/e2e/cli/context_test.go similarity index 97% rename from e2e/v1/cli/context_test.go rename to e2e/cli/context_test.go index 2663b3d3c..8d6df5a9c 100644 --- a/e2e/v1/cli/context_test.go +++ b/e2e/cli/context_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - e2e "github.com/tensorchord/envd/e2e/v1" + "github.com/tensorchord/envd/e2e" "github.com/tensorchord/envd/pkg/app" "github.com/tensorchord/envd/pkg/home" "github.com/tensorchord/envd/pkg/types" diff --git a/e2e/v1/cli/get_env_test.go b/e2e/cli/get_env_test.go similarity index 94% rename from e2e/v1/cli/get_env_test.go rename to e2e/cli/get_env_test.go index 368cda257..d068dde2a 100644 --- a/e2e/v1/cli/get_env_test.go +++ b/e2e/cli/get_env_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - e2e "github.com/tensorchord/envd/e2e/v1" + "github.com/tensorchord/envd/e2e" "github.com/tensorchord/envd/pkg/app" "github.com/tensorchord/envd/pkg/home" ) diff --git a/e2e/v0/cli/init_test.go b/e2e/cli/init_test.go similarity index 96% rename from e2e/v0/cli/init_test.go rename to e2e/cli/init_test.go index 0a3772055..8a8ca7b1b 100644 --- a/e2e/v0/cli/init_test.go +++ b/e2e/cli/init_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - e2e "github.com/tensorchord/envd/e2e/v1" + "github.com/tensorchord/envd/e2e" "github.com/tensorchord/envd/pkg/app" "github.com/tensorchord/envd/pkg/home" "github.com/tensorchord/envd/pkg/util/fileutil" diff --git a/e2e/cli/moby_test.go b/e2e/cli/moby_test.go new file mode 100644 index 000000000..0115759f5 --- /dev/null +++ b/e2e/cli/moby_test.go @@ -0,0 +1,63 @@ +// Copyright 2023 The envd 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 cli + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/tensorchord/envd/e2e" + "github.com/tensorchord/envd/pkg/home" + "github.com/tensorchord/envd/pkg/types" +) + +var _ = Describe("e2e moby builder test", Ordered, func() { + exampleName := "build-test" + defaultContext := "default" + mobyContext := "envd-test-moby" + ctx := types.Context{ + Name: mobyContext, + Builder: types.BuilderTypeMoby, + Runner: types.RunnerTypeDocker, + } + + BeforeAll(func() { + Expect(home.Initialize()).To(Succeed()) + err := home.GetManager().ContextCreate(ctx, true) + Expect(err).NotTo(HaveOccurred()) + }) + + It("should find a new context", func() { + ctx, err := home.GetManager().ContextGetCurrent() + Expect(err).NotTo(HaveOccurred()) + Expect(ctx.Name).To(Equal(mobyContext)) + Expect(ctx.Builder).To(Equal(types.BuilderTypeMoby)) + }) + + It("build images with moby", func() { + e := e2e.NewExample(e2e.BuildContextDirWithName(exampleName), "e2e") + e.BuildImage(true)() + }) + + AfterAll(func() { + err := home.GetManager().ContextUse(defaultContext) + Expect(err).NotTo(HaveOccurred()) + err = home.GetManager().ContextRemove(mobyContext) + Expect(err).NotTo(HaveOccurred()) + context, err := home.GetManager().ContextGetCurrent() + Expect(err).NotTo(HaveOccurred()) + Expect(context.Name).To(Equal(defaultContext)) + }) +}) diff --git a/e2e/v0/cli/quick_start_test.go b/e2e/cli/quick_start_test.go similarity index 93% rename from e2e/v0/cli/quick_start_test.go rename to e2e/cli/quick_start_test.go index 2e8048af9..6e5fc64a7 100644 --- a/e2e/v0/cli/quick_start_test.go +++ b/e2e/cli/quick_start_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - e2e "github.com/tensorchord/envd/e2e/v0" + "github.com/tensorchord/envd/e2e" ) var _ = Describe("e2e quickstart", Ordered, func() { diff --git a/e2e/v0/cli/suite_test.go b/e2e/cli/suite_test.go similarity index 96% rename from e2e/v0/cli/suite_test.go rename to e2e/cli/suite_test.go index d1c6671da..396f13ed5 100644 --- a/e2e/v0/cli/suite_test.go +++ b/e2e/cli/suite_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/e2e/cli/testdata/build-test/build.envd b/e2e/cli/testdata/build-test/build.envd new file mode 100644 index 000000000..27814172b --- /dev/null +++ b/e2e/cli/testdata/build-test/build.envd @@ -0,0 +1,8 @@ +# syntax=v1 + + +def build(): + base() + install.apt_packages(name=["htop"]) + install.conda() + install.python() diff --git a/e2e/v1/cli/testdata/custom-image-test/build.envd b/e2e/cli/testdata/custom-image-test/build.envd similarity index 75% rename from e2e/v1/cli/testdata/custom-image-test/build.envd rename to e2e/cli/testdata/custom-image-test/build.envd index 7f09c9344..869a7265e 100644 --- a/e2e/v1/cli/testdata/custom-image-test/build.envd +++ b/e2e/cli/testdata/custom-image-test/build.envd @@ -2,7 +2,7 @@ def build(): - base(image="python:3.9-slim", dev=False) + base(image="python:3.11-slim", dev=False) install.python_packages( name=[ "via", diff --git a/e2e/v1/cli/testdata/quick-start/build.envd b/e2e/cli/testdata/quick-start/build.envd similarity index 100% rename from e2e/v1/cli/testdata/quick-start/build.envd rename to e2e/cli/testdata/quick-start/build.envd diff --git a/e2e/v0/cli/testdata/quick-start/demo.py b/e2e/cli/testdata/quick-start/demo.py similarity index 100% rename from e2e/v0/cli/testdata/quick-start/demo.py rename to e2e/cli/testdata/quick-start/demo.py diff --git a/e2e/v1/cli/testdata/up-test/build.envd b/e2e/cli/testdata/up-test/build.envd similarity index 100% rename from e2e/v1/cli/testdata/up-test/build.envd rename to e2e/cli/testdata/up-test/build.envd diff --git a/e2e/v0/cli/up_test.go b/e2e/cli/up_test.go similarity index 96% rename from e2e/v0/cli/up_test.go rename to e2e/cli/up_test.go index 93280e42f..d3c296616 100644 --- a/e2e/v0/cli/up_test.go +++ b/e2e/cli/up_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - e2e "github.com/tensorchord/envd/e2e/v0" + "github.com/tensorchord/envd/e2e" "github.com/tensorchord/envd/pkg/app" "github.com/tensorchord/envd/pkg/driver/docker" "github.com/tensorchord/envd/pkg/envd" diff --git a/e2e/v1/docs/docs_gpu_test.go b/e2e/docs/docs_gpu_test.go similarity index 91% rename from e2e/v1/docs/docs_gpu_test.go rename to e2e/docs/docs_gpu_test.go index ce56d1369..8be7664ee 100644 --- a/e2e/v1/docs/docs_gpu_test.go +++ b/e2e/docs/docs_gpu_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ package docs import ( . "github.com/onsi/ginkgo/v2" - e2e "github.com/tensorchord/envd/e2e/v1" + "github.com/tensorchord/envd/e2e" ) var _ = Describe("check GPU examples in documentation", Ordered, func() { diff --git a/e2e/v0/docs/docs_test.go b/e2e/docs/docs_test.go similarity index 97% rename from e2e/v0/docs/docs_test.go rename to e2e/docs/docs_test.go index 5adadf4e6..9d17408f0 100644 --- a/e2e/v0/docs/docs_test.go +++ b/e2e/docs/docs_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - e2e "github.com/tensorchord/envd/e2e/v0" + "github.com/tensorchord/envd/e2e" "github.com/tensorchord/envd/pkg/app" "github.com/tensorchord/envd/pkg/home" ) diff --git a/e2e/docs/extra_lang_test.go b/e2e/docs/extra_lang_test.go new file mode 100644 index 000000000..23add40d1 --- /dev/null +++ b/e2e/docs/extra_lang_test.go @@ -0,0 +1,40 @@ +// Copyright 2023 The envd 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 docs + +import ( + . "github.com/onsi/ginkgo/v2" + + "github.com/tensorchord/envd/e2e" +) + +var _ = Describe("lang", Ordered, func() { + It("Should build rlang environment successfully", func() { + exampleName := "rlang" + testcase := "e2e-extra" + e := e2e.NewExample(e2e.BuildContextDirWithName(exampleName), testcase) + e.BuildImage(true)() + e.RunContainer()() + e.DestroyContainer()() + }) + It("Should build Julia environment successfully", func() { + exampleName := "julia" + testcase := "e2e-extra" + e := e2e.NewExample(e2e.BuildContextDirWithName(exampleName), testcase) + e.BuildImage(true)() + e.RunContainer()() + e.DestroyContainer()() + }) +}) diff --git a/e2e/v0/language/runtime_test.go b/e2e/docs/julia_mnist_test.go similarity index 67% rename from e2e/v0/language/runtime_test.go rename to e2e/docs/julia_mnist_test.go index d42c4b33c..f9525d171 100644 --- a/e2e/v0/language/runtime_test.go +++ b/e2e/docs/julia_mnist_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,29 +12,31 @@ // See the License for the specific language governing permissions and // limitations under the License. -package language +package docs import ( + "strconv" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - e2e "github.com/tensorchord/envd/e2e/v0" + "github.com/tensorchord/envd/e2e" ) -var _ = Describe("runtime", Ordered, func() { - exampleName := "runtime" +var _ = Describe("julia_mnist", Ordered, func() { + exampleName := "julia_mnist" testcase := "e2e" e := e2e.NewExample(e2e.BuildContextDirWithName(exampleName), testcase) BeforeAll(e.BuildImage(true)) BeforeEach(e.RunContainer()) - It("execute runtime command `numpy`", func() { - res, err := e.ExecRuntimeCommand("numpy") - Expect(err).To(BeNil()) - Expect(res).To(Equal("[2 3 4]")) - }) - It("execute runtime command `root`", func() { - _, err := e.ExecRuntimeCommand("root") + It("execute runtime command `julia-mnist`", func() { + res, err := e.ExecRuntimeCommand("julia-mnist") Expect(err).To(BeNil()) + IsNumber := func(s string) bool { + _, err = strconv.ParseFloat(s, 64) + return err == nil + } + Expect(res).To(Satisfy(IsNumber)) }) AfterEach(e.DestroyContainer()) }) diff --git a/e2e/v1/cli/quick_start_test.go b/e2e/docs/rlang_iris_test.go similarity index 70% rename from e2e/v1/cli/quick_start_test.go rename to e2e/docs/rlang_iris_test.go index 4a91fe92a..9c812e17c 100644 --- a/e2e/v1/cli/quick_start_test.go +++ b/e2e/docs/rlang_iris_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,25 +12,26 @@ // See the License for the specific language governing permissions and // limitations under the License. -package cli +package docs import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - e2e "github.com/tensorchord/envd/e2e/v1" + "github.com/tensorchord/envd/e2e" ) -var _ = Describe("e2e quickstart", Ordered, func() { - exampleName := "quick-start" +var _ = Describe("rlang_iris", Ordered, func() { + exampleName := "rlang_iris" testcase := "e2e" e := e2e.NewExample(e2e.BuildContextDirWithName(exampleName), testcase) BeforeAll(e.BuildImage(true)) BeforeEach(e.RunContainer()) - It("execute python demo.py", func() { - res, err := e.Exec("python demo.py") + It("execute runtime command `Rscript`", func() { + res, err := e.ExecRuntimeCommand("rlang-iris") Expect(err).To(BeNil()) - Expect(res).To(Equal("[2 3 4]")) + Expect(res).To(ContainSubstring("classif.acc")) + Expect(res).To(ContainSubstring("classif.ce")) }) AfterEach(e.DestroyContainer()) }) diff --git a/e2e/v1/docs/suite_test.go b/e2e/docs/suite_test.go similarity index 92% rename from e2e/v1/docs/suite_test.go rename to e2e/docs/suite_test.go index 000291872..aec839fdf 100644 --- a/e2e/v1/docs/suite_test.go +++ b/e2e/docs/suite_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -30,5 +30,5 @@ func init() { func TestMain(t *testing.T) { RegisterFailHandler(Fail) - RunSpecs(t, "envd cli Suite") + RunSpecs(t, "envd docs Suite") } diff --git a/e2e/v1/docs/testdata/complex/build.envd b/e2e/docs/testdata/complex/build.envd similarity index 50% rename from e2e/v1/docs/testdata/complex/build.envd rename to e2e/docs/testdata/complex/build.envd index 74975225c..5b73572af 100644 --- a/e2e/v1/docs/testdata/complex/build.envd +++ b/e2e/docs/testdata/complex/build.envd @@ -3,20 +3,20 @@ def build(): base(dev=True) - install.cuda(version="11.2.2", cudnn="8") + install.cuda(version="11.8.0", cudnn="8") install.conda() install.python() config.apt_source( source=""" -deb https://mirror.sjtu.edu.cn/ubuntu focal main restricted -deb https://mirror.sjtu.edu.cn/ubuntu focal-updates main restricted -deb https://mirror.sjtu.edu.cn/ubuntu focal universe -deb https://mirror.sjtu.edu.cn/ubuntu focal-updates universe -deb https://mirror.sjtu.edu.cn/ubuntu focal multiverse -deb https://mirror.sjtu.edu.cn/ubuntu focal-updates multiverse -deb https://mirror.sjtu.edu.cn/ubuntu focal-backports main restricted universe multiverse -deb http://archive.canonical.com/ubuntu focal partner -deb https://mirror.sjtu.edu.cn/ubuntu focal-security main restricted universe multiverse +deb https://mirror.sjtu.edu.cn/ubuntu jammy main restricted +deb https://mirror.sjtu.edu.cn/ubuntu jammy-updates main restricted +deb https://mirror.sjtu.edu.cn/ubuntu jammy universe +deb https://mirror.sjtu.edu.cn/ubuntu jammy-updates universe +deb https://mirror.sjtu.edu.cn/ubuntu jammy multiverse +deb https://mirror.sjtu.edu.cn/ubuntu jammy-updates multiverse +deb https://mirror.sjtu.edu.cn/ubuntu jammy-backports main restricted universe multiverse +deb http://archive.canonical.com/ubuntu jammy partner +deb https://mirror.sjtu.edu.cn/ubuntu jammy-security main restricted universe multiverse """ ) config.pip_index(url="https://mirror.sjtu.edu.cn/pypi/web/simple") diff --git a/e2e/v1/docs/testdata/envdlib/build.envd b/e2e/docs/testdata/envdlib/build.envd similarity index 100% rename from e2e/v1/docs/testdata/envdlib/build.envd rename to e2e/docs/testdata/envdlib/build.envd diff --git a/e2e/v1/docs/testdata/getting_started/build.envd b/e2e/docs/testdata/getting_started/build.envd similarity index 100% rename from e2e/v1/docs/testdata/getting_started/build.envd rename to e2e/docs/testdata/getting_started/build.envd diff --git a/e2e/v1/docs/testdata/julia/build.envd b/e2e/docs/testdata/julia/build.envd similarity index 100% rename from e2e/v1/docs/testdata/julia/build.envd rename to e2e/docs/testdata/julia/build.envd diff --git a/e2e/docs/testdata/julia_mnist/build.envd b/e2e/docs/testdata/julia_mnist/build.envd new file mode 100644 index 000000000..d8ee4b08a --- /dev/null +++ b/e2e/docs/testdata/julia_mnist/build.envd @@ -0,0 +1,9 @@ +# syntax=v1 + + +def build(): + base(dev=True) + install.julia() + install.julia_packages(name=["Flux", "MLDatasets"]) + runtime.command(commands={"julia-mnist": "julia mlp_mnist.jl"}) + runtime.environ(env={"DATADEPS_ALWAYS_ACCEPT": "true"}) diff --git a/e2e/docs/testdata/julia_mnist/mlp_mnist.jl b/e2e/docs/testdata/julia_mnist/mlp_mnist.jl new file mode 100644 index 000000000..26e0e3cef --- /dev/null +++ b/e2e/docs/testdata/julia_mnist/mlp_mnist.jl @@ -0,0 +1,85 @@ +# https://github.com/FluxML/model-zoo/blob/master/vision/mlp_mnist/mlp_mnist.jl +# License: MIT Copyright (c) 2017 by Flux contributors + +# Simple multi-layer perceptron, for the MNIST hand-written digits. +# This example does not use a GPU, it's small enough not to need one. + +using Flux, MLDatasets, Statistics + +# Our model is very simple: Its one "hidden layer" has 32 "neurons" each connected to every input pixel. +# Each has a sigmoid nonlinearity, and is connected to every "neuron" in the output layer. +# Finally, softmax produces probabilities, i.e. positive numbers which add up to 1: + +model = Chain(Dense(28^2 => 32, sigmoid), Dense(32 => 10), softmax) + +#===== DATA =====# + +# Calling MLDatasets.MNIST() will download the dataset if necessary, +# and return a struct containing it. +# It takes a few seconds to read from disk each time, so do this once: + +train_data = MLDatasets.MNIST() # i.e. split=:train +test_data = MLDatasets.MNIST(split=:test) + +# train_data.features is a 28ร—28ร—60000 Array{Float32, 3} of the images. +# We need a 2D array for our model. Let's combine the reshape needed with +# other pre-processing, in a function: + +function simple_loader(data::MNIST; batchsize::Int=64) + x2dim = reshape(data.features, 28^2, :) + yhot = Flux.onehotbatch(data.targets, 0:9) + Flux.DataLoader((x2dim, yhot); batchsize, shuffle=true) +end + +# train_data.targets is a 60000-element Vector{Int}, of labels from 0 to 9. +# Flux.onehotbatch([0,1,9], 0:9) makes a matrix of 0 and 1. + +simple_loader(train_data) # returns a DataLoader, with first element a tuple like this: + +x1, y1 = first(simple_loader(train_data)); # (784ร—64 Matrix{Float32}, 10ร—64 OneHotMatrix) + +model(x1) # x1 is the right shape for our model + +y1 # y1 is the same shape as the model output. + +# @show Flux.crossentropy(model(x1), y1); # This will be our loss function + +#===== ACCURACY =====# + +# We're going to log accuracy and loss during training. There's no advantage to +# calculating these on minibatches, since MNIST is small enough to do it at once. + +function simple_accuracy(model, data::MNIST=test_data) + (x, y) = only(simple_loader(data; batchsize=length(data))) # make one big batch + y_hat = model(x) + iscorrect = Flux.onecold(y_hat) .== Flux.onecold(y) # BitVector + acc = round(100 * mean(iscorrect); digits=2) +end + +# @show simple_accuracy(model); # accuracy about 10%, on training data, before training! + +#===== TRAINING =====# + +# Make a dataloader using the desired batchsize: + +train_loader = simple_loader(train_data, batchsize = 256) + +# Initialise storage needed for the Adam optimiser, with our chosen learning rate: + +opt_state = Flux.setup(Adam(3e-4), model); + +# Then train for 3 epochs, printing out details as we go: + +for epoch in 1:3 + loss = 0.0 + for (x, y) in train_loader + # Compute the loss and the gradients: + l, gs = Flux.withgradient(m -> Flux.crossentropy(m(x), y), model) + # Update the model parameters (and the Adam momenta): + Flux.update!(opt_state, model, gs[1]) + # Accumulate the mean loss, just for logging: + loss += l / length(train_loader) + end +end + +print(simple_accuracy(model, test_data)) diff --git a/e2e/v1/docs/testdata/jupyter/build.envd b/e2e/docs/testdata/jupyter/build.envd similarity index 100% rename from e2e/v1/docs/testdata/jupyter/build.envd rename to e2e/docs/testdata/jupyter/build.envd diff --git a/e2e/v1/docs/testdata/minimal/build.envd b/e2e/docs/testdata/minimal/build.envd similarity index 100% rename from e2e/v1/docs/testdata/minimal/build.envd rename to e2e/docs/testdata/minimal/build.envd diff --git a/e2e/v1/docs/testdata/rlang/build.envd b/e2e/docs/testdata/rlang/build.envd similarity index 62% rename from e2e/v1/docs/testdata/rlang/build.envd rename to e2e/docs/testdata/rlang/build.envd index 1809a2375..cbd050eee 100644 --- a/e2e/v1/docs/testdata/rlang/build.envd +++ b/e2e/docs/testdata/rlang/build.envd @@ -4,4 +4,4 @@ def build(): base(dev=True) install.r_lang() - install.r_packages(name=["drat"]) + install.r_packages(name=["remotes"]) diff --git a/e2e/docs/testdata/rlang_iris/build.envd b/e2e/docs/testdata/rlang_iris/build.envd new file mode 100644 index 000000000..7e6af268d --- /dev/null +++ b/e2e/docs/testdata/rlang_iris/build.envd @@ -0,0 +1,9 @@ +# syntax=v1 + + +def build(): + base(dev=True) + install.apt_packages(name=["build-essential"]) + install.r_lang() + install.r_packages(name=["mlr3", "mlr3learners"]) + runtime.command(commands={"rlang-iris": "Rscript iris.r 2> /dev/null"}) diff --git a/e2e/docs/testdata/rlang_iris/iris.r b/e2e/docs/testdata/rlang_iris/iris.r new file mode 100644 index 000000000..38b9007d7 --- /dev/null +++ b/e2e/docs/testdata/rlang_iris/iris.r @@ -0,0 +1,36 @@ +# Example from https://github.com/mlr-org/mlr3gallery +# MIT License Copyright (c) 2019 mlr-org + +library(mlr3learners) + +# creates mlr3 task from scratch, from a data.frame +# 'target' names the column in the dataset we want to learn to predict +task = as_task_classif(iris, target = "Species") +# in this case we could also take the iris example from mlr3's dictionary of shipped example tasks +# 2 equivalent calls to create a task. The second is just sugar for the user. +task = mlr_tasks$get("iris") +task = tsk("iris") +# print(task) +# create learner from dictionary of mlr3learners +# 2 equivalent calls: +learner_1 = mlr_learners$get("classif.rpart") +learner_1 = lrn("classif.rpart") +# print(learner_1) + +# train learner on subset of task +learner_1$train(task, row_ids = 1:120) +# this is what the decision tree looks like +# print(learner_1$model) +# predict using observations from task +prediction = learner_1$predict(task, row_ids = 121:150) +# predict using "new" observations from an external data.frame +prediction = learner_1$predict_newdata(newdata = iris[121:150, ]) +# print(prediction) + +# head(as.data.table(mlr_measures)) +scores = prediction$score(msr("classif.acc")) +print(scores) +scores = prediction$score(msrs(c("classif.acc", "classif.ce"))) +print(scores) +cm = prediction$confusion +print(cm) diff --git a/e2e/v1/e2e_helper.go b/e2e/e2e_helper.go similarity index 99% rename from e2e/v1/e2e_helper.go rename to e2e/e2e_helper.go index 09e521d00..462d52549 100644 --- a/e2e/v1/e2e_helper.go +++ b/e2e/e2e_helper.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/e2e/v1/language/python_test.go b/e2e/language/python_test.go similarity index 64% rename from e2e/v1/language/python_test.go rename to e2e/language/python_test.go index 27e7b9e8d..76509eb06 100644 --- a/e2e/v1/language/python_test.go +++ b/e2e/language/python_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,14 +16,15 @@ package language import ( . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" - e2e "github.com/tensorchord/envd/e2e/v1" + "github.com/tensorchord/envd/e2e" ) var _ = Describe("python", Ordered, func() { + testcase := "e2e" It("Should build packages successfully", func() { exampleName := "python/packages" - testcase := "e2e" e := e2e.NewExample(e2e.BuildContextDirWithName(exampleName), testcase) e.BuildImage(true)() e.RunContainer()() @@ -31,7 +32,6 @@ var _ = Describe("python", Ordered, func() { }) It("Should build requirements successfully", func() { exampleName := "python/requirements" - testcase := "e2e" e := e2e.NewExample(e2e.BuildContextDirWithName(exampleName), testcase) e.BuildImage(true)() e.RunContainer()() @@ -39,7 +39,6 @@ var _ = Describe("python", Ordered, func() { }) It("Should build hybrid successfully", func() { exampleName := "python/hybrid" - testcase := "e2e" e := e2e.NewExample(e2e.BuildContextDirWithName(exampleName), testcase) e.BuildImage(true)() e.RunContainer()() @@ -48,7 +47,6 @@ var _ = Describe("python", Ordered, func() { It("Should build conda with channel successfully", func() { exampleName := "python/conda" - testcase := "e2e" e := e2e.NewExample(e2e.BuildContextDirWithName(exampleName), testcase) e.BuildImage(true)() e.RunContainer()() @@ -57,10 +55,35 @@ var _ = Describe("python", Ordered, func() { It("Should build conda with separate channel setting successfully", func() { exampleName := "python/conda_channel" - testcase := "e2e" e := e2e.NewExample(e2e.BuildContextDirWithName(exampleName), testcase) e.BuildImage(true)() e.RunContainer()() e.DestroyContainer()() }) + + Describe("Should build uv with Python successfully", func() { + exampleName := "python/uv" + e := e2e.NewExample(e2e.BuildContextDirWithName(exampleName), testcase) + BeforeAll(e.BuildImage(true)) + BeforeEach(e.RunContainer()) + It("Should have Python installed", func() { + res, err := e.ExecRuntimeCommand("uv-python") + Expect(err).To(BeNil()) + Expect(res).To(ContainSubstring("python")) + }) + AfterEach(e.DestroyContainer()) + }) + + Describe("Should build pixi with Python successfully", func() { + exampleName := "python/pixi" + e := e2e.NewExample(e2e.BuildContextDirWithName(exampleName), testcase) + BeforeAll(e.BuildImage(true)) + BeforeEach(e.RunContainer()) + It("Should have Python and dependencies installed", func() { + res, err := e.ExecRuntimeCommand("pixi-via") + Expect(err).To(BeNil()) + Expect(res).To(ContainSubstring("via args parser")) + }) + AfterEach(e.DestroyContainer()) + }) }) diff --git a/e2e/v1/language/runtime_test.go b/e2e/language/runtime_test.go similarity index 93% rename from e2e/v1/language/runtime_test.go rename to e2e/language/runtime_test.go index c736e0a43..9d8b42e71 100644 --- a/e2e/v1/language/runtime_test.go +++ b/e2e/language/runtime_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - e2e "github.com/tensorchord/envd/e2e/v1" + "github.com/tensorchord/envd/e2e" ) var _ = Describe("runtime", Ordered, func() { diff --git a/e2e/v1/language/suite_test.go b/e2e/language/suite_test.go similarity index 96% rename from e2e/v1/language/suite_test.go rename to e2e/language/suite_test.go index 2d815693b..5fdc07a3b 100644 --- a/e2e/v1/language/suite_test.go +++ b/e2e/language/suite_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/e2e/v1/language/testdata/python/conda/build.envd b/e2e/language/testdata/python/conda/build.envd similarity index 100% rename from e2e/v1/language/testdata/python/conda/build.envd rename to e2e/language/testdata/python/conda/build.envd diff --git a/e2e/v0/language/testdata/python/conda/env.yaml b/e2e/language/testdata/python/conda/env.yaml similarity index 100% rename from e2e/v0/language/testdata/python/conda/env.yaml rename to e2e/language/testdata/python/conda/env.yaml diff --git a/e2e/v0/language/testdata/python/requirements/setup.py b/e2e/language/testdata/python/conda/setup.py similarity index 84% rename from e2e/v0/language/testdata/python/requirements/setup.py rename to e2e/language/testdata/python/conda/setup.py index 0be13a508..d69175316 100644 --- a/e2e/v0/language/testdata/python/requirements/setup.py +++ b/e2e/language/testdata/python/conda/setup.py @@ -1,4 +1,4 @@ -from setuptools import setup, find_packages +from setuptools import find_packages, setup setup( name="envd_e2e_conda_test", diff --git a/e2e/v1/language/testdata/python/conda_channel/build.envd b/e2e/language/testdata/python/conda_channel/build.envd similarity index 100% rename from e2e/v1/language/testdata/python/conda_channel/build.envd rename to e2e/language/testdata/python/conda_channel/build.envd diff --git a/e2e/v0/language/testdata/python/conda_channel/env.yaml b/e2e/language/testdata/python/conda_channel/env.yaml similarity index 100% rename from e2e/v0/language/testdata/python/conda_channel/env.yaml rename to e2e/language/testdata/python/conda_channel/env.yaml diff --git a/e2e/v1/language/testdata/python/hybrid/build.envd b/e2e/language/testdata/python/hybrid/build.envd similarity index 100% rename from e2e/v1/language/testdata/python/hybrid/build.envd rename to e2e/language/testdata/python/hybrid/build.envd diff --git a/e2e/v0/language/testdata/python/hybrid/requirements.txt b/e2e/language/testdata/python/hybrid/requirements.txt similarity index 100% rename from e2e/v0/language/testdata/python/hybrid/requirements.txt rename to e2e/language/testdata/python/hybrid/requirements.txt diff --git a/e2e/v1/language/testdata/python/packages/build.envd b/e2e/language/testdata/python/packages/build.envd similarity index 100% rename from e2e/v1/language/testdata/python/packages/build.envd rename to e2e/language/testdata/python/packages/build.envd diff --git a/e2e/language/testdata/python/pixi/build.envd b/e2e/language/testdata/python/pixi/build.envd new file mode 100644 index 000000000..b31fa863e --- /dev/null +++ b/e2e/language/testdata/python/pixi/build.envd @@ -0,0 +1,9 @@ +def build(): + base(dev=True) + install.pixi(use_pixi_mirror=True) + shell("fish") + runtime.command( + commands={ + "pixi-via": "pixi run -- via --help", + } + ) diff --git a/e2e/language/testdata/python/pixi/pixi.toml b/e2e/language/testdata/python/pixi/pixi.toml new file mode 100644 index 000000000..9af26bdd9 --- /dev/null +++ b/e2e/language/testdata/python/pixi/pixi.toml @@ -0,0 +1,13 @@ +[workspace] +channels = ["conda-forge"] +name = "envd_pixi_test" +platforms = ["linux-64"] +version = "0.1.0" + +[tasks] + +[dependencies] +python = "3.11.*" + +[pypi-dependencies] +via = ">=0.1.2, <0.2" diff --git a/e2e/v1/language/testdata/python/requirements/build.envd b/e2e/language/testdata/python/requirements/build.envd similarity index 100% rename from e2e/v1/language/testdata/python/requirements/build.envd rename to e2e/language/testdata/python/requirements/build.envd diff --git a/e2e/v0/language/testdata/python/requirements/requirements.txt b/e2e/language/testdata/python/requirements/requirements.txt similarity index 100% rename from e2e/v0/language/testdata/python/requirements/requirements.txt rename to e2e/language/testdata/python/requirements/requirements.txt diff --git a/e2e/v1/language/testdata/python/conda/setup.py b/e2e/language/testdata/python/requirements/setup.py similarity index 84% rename from e2e/v1/language/testdata/python/conda/setup.py rename to e2e/language/testdata/python/requirements/setup.py index 0be13a508..d69175316 100644 --- a/e2e/v1/language/testdata/python/conda/setup.py +++ b/e2e/language/testdata/python/requirements/setup.py @@ -1,4 +1,4 @@ -from setuptools import setup, find_packages +from setuptools import find_packages, setup setup( name="envd_e2e_conda_test", diff --git a/e2e/language/testdata/python/uv/build.envd b/e2e/language/testdata/python/uv/build.envd new file mode 100644 index 000000000..03d500668 --- /dev/null +++ b/e2e/language/testdata/python/uv/build.envd @@ -0,0 +1,12 @@ +# syntax=v1 + + +def build(): + base(dev=True) + install.uv() + shell("fish") + runtime.command( + commands={ + "uv-python": "uv python list --only-installed", + } + ) diff --git a/e2e/v1/language/testdata/run/build.envd b/e2e/language/testdata/run/build.envd similarity index 100% rename from e2e/v1/language/testdata/run/build.envd rename to e2e/language/testdata/run/build.envd diff --git a/e2e/v1/language/testdata/runtime/build.envd b/e2e/language/testdata/runtime/build.envd similarity index 100% rename from e2e/v1/language/testdata/runtime/build.envd rename to e2e/language/testdata/runtime/build.envd diff --git a/e2e/v0/language/testdata/runtime/demo.py b/e2e/language/testdata/runtime/demo.py similarity index 100% rename from e2e/v0/language/testdata/runtime/demo.py rename to e2e/language/testdata/runtime/demo.py diff --git a/e2e/v0/cli/build_test.go b/e2e/v0/cli/build_test.go deleted file mode 100644 index 0bb926405..000000000 --- a/e2e/v0/cli/build_test.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2022 The envd 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 cli - -import ( - "context" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - e2e "github.com/tensorchord/envd/e2e/v0" - "github.com/tensorchord/envd/pkg/app" - "github.com/tensorchord/envd/pkg/driver/docker" - "github.com/tensorchord/envd/pkg/envd" - "github.com/tensorchord/envd/pkg/home" - "github.com/tensorchord/envd/pkg/types" -) - -var _ = Describe("build command", Ordered, func() { - buildTestName := "testdata/build-test" - customImageTestName := "testdata/custom-image-test" - When("given the right arguments", func() { - It("should build successfully", func() { - envdApp := app.New() - e2e.ResetEnvdApp() - err := envdApp.Run([]string{"envd.test", "--debug", "bootstrap"}) - Expect(err).NotTo(HaveOccurred()) - _, err = docker.NewClient(context.TODO()) - Expect(err).NotTo(HaveOccurred()) - c := types.Context{Runner: types.RunnerTypeDocker} - opt := envd.Options{Context: &c} - envdEngine, err := envd.New(context.TODO(), opt) - Expect(err).NotTo(HaveOccurred()) - _, err = envdEngine.Destroy(context.TODO(), buildTestName) - Expect(err).NotTo(HaveOccurred()) - envdApp = app.New() - args := []string{ - "envd.test", "--debug", "build", "--path", buildTestName, - } - err = envdApp.Run(args) - Expect(err).NotTo(HaveOccurred()) - }) - }) - When("given the custom image", func() { - It("should build successfully", func() { - envdApp := app.New() - e2e.ResetEnvdApp() - err := envdApp.Run([]string{"envd.test", "--debug", "bootstrap"}) - Expect(err).NotTo(HaveOccurred()) - _, err = docker.NewClient(context.TODO()) - Expect(err).NotTo(HaveOccurred()) - c := types.Context{Runner: types.RunnerTypeDocker} - opt := envd.Options{Context: &c} - envdEngine, err := envd.New(context.TODO(), opt) - Expect(err).NotTo(HaveOccurred()) - _, err = envdEngine.Destroy(context.TODO(), buildTestName) - Expect(err).NotTo(HaveOccurred()) - envdApp = app.New() - args := []string{ - "envd.test", "--debug", "build", "--path", customImageTestName, - } - err = envdApp.Run(args) - Expect(err).NotTo(HaveOccurred()) - }) - }) - AfterAll(func() { - Expect(home.Initialize()).NotTo(HaveOccurred()) - envdApp := app.New() - e2e.ResetEnvdApp() - err := envdApp.Run([]string{"envd.test", "--debug", "bootstrap"}) - Expect(err).NotTo(HaveOccurred()) - _, err = docker.NewClient(context.TODO()) - Expect(err).NotTo(HaveOccurred()) - c := types.Context{Runner: types.RunnerTypeDocker} - opt := envd.Options{Context: &c} - envdEngine, err := envd.New(context.TODO(), opt) - Expect(err).NotTo(HaveOccurred()) - _, err = envdEngine.Destroy(context.TODO(), buildTestName) - Expect(err).NotTo(HaveOccurred()) - // Init DefaultGraph. - // ir.DefaultGraph = ir.NewGraph() - }) -}) diff --git a/e2e/v0/cli/context_test.go b/e2e/v0/cli/context_test.go deleted file mode 100644 index 93f2256d2..000000000 --- a/e2e/v0/cli/context_test.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2022 The envd 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 cli - -import ( - "os/exec" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - e2e "github.com/tensorchord/envd/e2e/v0" - "github.com/tensorchord/envd/pkg/app" - "github.com/tensorchord/envd/pkg/home" - "github.com/tensorchord/envd/pkg/types" -) - -var _ = Describe("home context", func() { - defaultContext := "default" - BeforeEach(func() { - Expect(home.Initialize()).To(Succeed()) - }) - When("check the default context", func() { - It("should found the default context", func() { - contexts, err := home.GetManager().ContextList() - Expect(err).NotTo(HaveOccurred()) - Expect(contexts.Current).To(Equal(defaultContext)) - }) - }) - - Describe("add a new context", Ordered, func() { - testContext := "envd_home_test" - testBuilderAddress := "0.0.0.0:12345" - testBuilder := types.BuilderTypeTCP - testRunner := types.RunnerTypeEnvdServer - testRunnerAddress := "http://localhost" - c := types.Context{ - Name: testContext, - Builder: testBuilder, - BuilderAddress: testBuilderAddress, - Runner: testRunner, - RunnerAddress: &testRunnerAddress, - } - - BeforeAll(func() { - err := home.GetManager().ContextCreate(c, true) - Expect(err).NotTo(HaveOccurred()) - }) - - It("should find a new context", func() { - contexts, err := home.GetManager().ContextList() - Expect(err).NotTo(HaveOccurred()) - Expect(contexts.Current).To(Equal(testContext)) - }) - - Describe("connect buildkit through TCP", Ordered, func() { - name := "envd-buildkitd-tcp-test" - buildContext := "testdata/build-test" - dockerArgs := []string{ - "run", "-d", "-p", "12345:8000", "--rm", "--name", name, - "--security-opt", "seccomp=unconfined", "--security-opt", "apparmor=unconfined", - "moby/buildkit:rootless", "--addr", "tcp://0.0.0.0:8000", "--oci-worker-no-process-sandbox", - } - BeforeAll(func() { - cmd := exec.Command("docker", dockerArgs...) - err := cmd.Run() - Expect(err).ToNot(HaveOccurred()) - }) - - It("should be able to build image with TCP context", func() { - args := []string{"envd.test", "--debug", "build", "--path", buildContext} - envdApp := app.New() - e2e.ResetEnvdApp() - err := envdApp.Run(args) - Expect(err).NotTo(HaveOccurred()) - }) - - AfterAll(func() { - cmd := exec.Command("docker", "stop", name) - err := cmd.Run() - Expect(err).ToNot(HaveOccurred()) - }) - }) - - It("fail to delete the current context", func() { - err := home.GetManager().ContextRemove(testContext) - Expect(err).To(HaveOccurred()) - }) - - AfterAll(func() { - err := home.GetManager().ContextUse(defaultContext) - Expect(err).NotTo(HaveOccurred()) - err = home.GetManager().ContextRemove(testContext) - Expect(err).NotTo(HaveOccurred()) - contexts, err := home.GetManager().ContextList() - Expect(err).NotTo(HaveOccurred()) - Expect(contexts.Current).To(Equal(defaultContext)) - }) - }) -}) diff --git a/e2e/v0/cli/get_env_test.go b/e2e/v0/cli/get_env_test.go deleted file mode 100644 index 7b2e9194b..000000000 --- a/e2e/v0/cli/get_env_test.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2022 The envd 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 cli - -import ( - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - e2e "github.com/tensorchord/envd/e2e/v0" - "github.com/tensorchord/envd/pkg/app" - "github.com/tensorchord/envd/pkg/home" -) - -var _ = Describe("get env command", func() { - args := []string{ - "envd.test", "--debug", "envs", "list", - } - BeforeEach(func() { - Expect(home.Initialize()).NotTo(HaveOccurred()) - envdApp := app.New() - err := envdApp.Run([]string{"envd.test", "--debug", "bootstrap"}) - Expect(err).NotTo(HaveOccurred()) - }) - When("given the right arguments", func() { - It("should get the environments successfully", func() { - envdApp := app.New() - e2e.ResetEnvdApp() - err := envdApp.Run(args) - Expect(err).NotTo(HaveOccurred()) - }) - }) -}) diff --git a/e2e/v0/cli/testdata/build-test/build.envd b/e2e/v0/cli/testdata/build-test/build.envd deleted file mode 100644 index 3b22de80e..000000000 --- a/e2e/v0/cli/testdata/build-test/build.envd +++ /dev/null @@ -1,2 +0,0 @@ -def build(): - base(os="ubuntu20.04", language="python3") diff --git a/e2e/v0/cli/testdata/custom-image-test/build.envd b/e2e/v0/cli/testdata/custom-image-test/build.envd deleted file mode 100644 index 6c295247e..000000000 --- a/e2e/v0/cli/testdata/custom-image-test/build.envd +++ /dev/null @@ -1,8 +0,0 @@ -def build(): - base(language="python", image="python:3.9-slim") - install.python_packages( - name=[ - "via", - ] - ) - config.entrypoint(["date", "-u"]) diff --git a/e2e/v0/cli/testdata/quick-start/build.envd b/e2e/v0/cli/testdata/quick-start/build.envd deleted file mode 100644 index 9c0e3cb02..000000000 --- a/e2e/v0/cli/testdata/quick-start/build.envd +++ /dev/null @@ -1,8 +0,0 @@ -def build(): - base(os="ubuntu20.04", language="python3") - install.python_packages( - name=[ - "numpy", - ] - ) - shell("zsh") diff --git a/e2e/v0/cli/testdata/up-test/build.envd b/e2e/v0/cli/testdata/up-test/build.envd deleted file mode 100644 index a71119306..000000000 --- a/e2e/v0/cli/testdata/up-test/build.envd +++ /dev/null @@ -1,12 +0,0 @@ -def build(): - base(os="ubuntu20.04", language="python3") - install.python_packages( - name=[ - "via", - ] - ) - install.apt_packages(name=["screenfetch"]) - shell("zsh") - config.pip_index(url="https://pypi.tuna.tsinghua.edu.cn/simple") - # git_config(name="envd", email="envd@envd", editor="vim") - install.vscode_extensions(["ms-python.python"]) diff --git a/e2e/v0/docs/docs_gpu_test.go b/e2e/v0/docs/docs_gpu_test.go deleted file mode 100644 index 654b5bfc0..000000000 --- a/e2e/v0/docs/docs_gpu_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2022 The envd 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 docs - -import ( - . "github.com/onsi/ginkgo/v2" - - e2e "github.com/tensorchord/envd/e2e/v0" -) - -var _ = Describe("check GPU examples in documentation", Ordered, func() { - e := e2e.NewExample(e2e.BuildContextDirWithName("complex"), "e2e-doc") - It("should be able to build the GPU example", e.BuildImage(true)) - AfterAll(e.DestroyContainer()) -}) diff --git a/e2e/v0/docs/testdata/complex/build.envd b/e2e/v0/docs/testdata/complex/build.envd deleted file mode 100644 index 99dbeb9ad..000000000 --- a/e2e/v0/docs/testdata/complex/build.envd +++ /dev/null @@ -1,31 +0,0 @@ -def build(): - config.apt_source( - source=""" -deb https://mirror.sjtu.edu.cn/ubuntu focal main restricted -deb https://mirror.sjtu.edu.cn/ubuntu focal-updates main restricted -deb https://mirror.sjtu.edu.cn/ubuntu focal universe -deb https://mirror.sjtu.edu.cn/ubuntu focal-updates universe -deb https://mirror.sjtu.edu.cn/ubuntu focal multiverse -deb https://mirror.sjtu.edu.cn/ubuntu focal-updates multiverse -deb https://mirror.sjtu.edu.cn/ubuntu focal-backports main restricted universe multiverse -deb http://archive.canonical.com/ubuntu focal partner -deb https://mirror.sjtu.edu.cn/ubuntu focal-security main restricted universe multiverse -""" - ) - config.pip_index(url="https://mirror.sjtu.edu.cn/pypi/web/simple") - install.vscode_extensions( - [ - "ms-python.python", - ] - ) - base(os="ubuntu20.04", language="python3") - install.python_packages( - name=[ - "numpy", - ] - ) - install.cuda(version="11.2.2", cudnn="8") - shell("zsh") - install.apt_packages(name=["htop"]) - git_config(name="Ce Gao", email="cegao@tensorchord.ai", editor="vim") - run(["ls -la"]) diff --git a/e2e/v0/docs/testdata/envdlib/build.envd b/e2e/v0/docs/testdata/envdlib/build.envd deleted file mode 100644 index cd5b98c37..000000000 --- a/e2e/v0/docs/testdata/envdlib/build.envd +++ /dev/null @@ -1,6 +0,0 @@ -envdlib = include("https://github.com/tensorchord/envdlib") - - -def build(): - base(os="ubuntu20.04", language="python") - envdlib.tensorboard(host_port=8888) diff --git a/e2e/v0/docs/testdata/getting_started/build.envd b/e2e/v0/docs/testdata/getting_started/build.envd deleted file mode 100644 index 4c7cbf7c3..000000000 --- a/e2e/v0/docs/testdata/getting_started/build.envd +++ /dev/null @@ -1,10 +0,0 @@ -def build(): - base(os="ubuntu20.04", language="python3") - # Configure pip index if needed. - # config.pip_index(url = "https://pypi.tuna.tsinghua.edu.cn/simple") - install.python_packages( - name=[ - "numpy", - ] - ) - shell("zsh") diff --git a/e2e/v0/docs/testdata/jupyter/build.envd b/e2e/v0/docs/testdata/jupyter/build.envd deleted file mode 100644 index 3f3da5fa6..000000000 --- a/e2e/v0/docs/testdata/jupyter/build.envd +++ /dev/null @@ -1,9 +0,0 @@ -def build(): - base(os="ubuntu20.04", language="python3") - install.python_packages( - name=[ - "numpy", - ] - ) - shell("zsh") - config.jupyter(token="") diff --git a/e2e/v0/docs/testdata/minimal/build.envd b/e2e/v0/docs/testdata/minimal/build.envd deleted file mode 100644 index 26742cd5a..000000000 --- a/e2e/v0/docs/testdata/minimal/build.envd +++ /dev/null @@ -1,7 +0,0 @@ -def build(): - base(os="ubuntu20.04", language="python3") - install.python_packages( - name=[ - "via", - ] - ) diff --git a/e2e/v0/e2e_helper.go b/e2e/v0/e2e_helper.go deleted file mode 100644 index 47add9241..000000000 --- a/e2e/v0/e2e_helper.go +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2022 The envd 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 e2e - -import ( - "bytes" - "context" - "path/filepath" - "strings" - - "github.com/cockroachdb/errors" - "github.com/sirupsen/logrus" - - "github.com/tensorchord/envd/pkg/app" - "github.com/tensorchord/envd/pkg/driver/docker" - "github.com/tensorchord/envd/pkg/envd" - ir "github.com/tensorchord/envd/pkg/lang/ir/v0" - "github.com/tensorchord/envd/pkg/types" -) - -func BuildContextDirWithName(name string) string { - return filepath.Join("testdata", name) -} - -func ResetEnvdApp() { - ir.DefaultGraph = ir.NewGraph() -} - -func (e *Example) BuildImage(force bool) func() { - return func() { - logrus.Infof("building %s image in %s", e.Name, e.BuildContextPath) - args := []string{ - "envd.test", "--debug", "build", - "--path", e.BuildContextPath, "--tag", e.Tag, - } - if force { - args = append(args, "--force") - } - ResetEnvdApp() - err := e.app.Run(args) - if err != nil { - panic(err) - } - } -} - -func (e *Example) RemoveImage() func() { - return func() { - ctx := context.TODO() - dockerClient, err := docker.NewClient(ctx) - if err != nil { - panic(err) - } - err = dockerClient.RemoveImage(ctx, e.Tag) - if err != nil { - panic(err) - } - } -} - -func GetEngine(ctx context.Context) envd.Engine { - opt := envd.Options{ - Context: &types.Context{ - Runner: types.RunnerTypeDocker, - }, - } - engine, err := envd.New(ctx, opt) - if err != nil { - panic(err) - } - return engine -} - -type Example struct { - Tag string - BuildContextPath string - // Name is the filepath.Base(BuildContextPath). - Name string - - app *app.EnvdApp -} - -func NewExample(path string, testcaseAbbr string) *Example { - name := filepath.Base(path) - tag := name + ":" + testcaseAbbr - app := app.New() - return &Example{ - Tag: tag, - Name: name, - BuildContextPath: path, - app: &app, - } -} - -func (e *Example) Exec(cmd string) (string, error) { - args := []string{ - "envd.test", "exec", "--name", e.Name, "--raw", cmd, - } - - buffer := new(bytes.Buffer) - e.app.Writer = buffer - - ResetEnvdApp() - err := e.app.Run(args) - if err != nil { - return "", errors.Wrap(err, "failed to start `run` command") - } - return strings.Trim(buffer.String(), "\n"), nil -} - -func (e *Example) ExecRuntimeCommand(cmd string) (string, error) { - buildContext := e.BuildContextPath - args := []string{ - "envd.test", "--debug", "exec", "-p", buildContext, "--command", cmd, - } - - buffer := new(bytes.Buffer) - e.app.Writer = buffer - - ResetEnvdApp() - err := e.app.Run(args) - if err != nil { - return "", errors.Wrap(err, "failed to start `run` command") - } - return strings.Trim(buffer.String(), "\n"), nil -} - -func (e *Example) RunContainer() func() { - return func() { - buildContext := e.BuildContextPath - args := []string{ - "envd.test", "--debug", "up", "--path", buildContext, "--tag", e.Tag, "--detach", "--force", - } - ResetEnvdApp() - err := e.app.Run(args) - if err != nil { - panic(err) - } - } -} - -func (e *Example) DestroyContainer() func() { - return func() { - buildContext := e.BuildContextPath - args := []string{ - "envd.test", "--debug", "destroy", "--path", buildContext, - } - err := e.app.Run(args) - if err != nil { - panic(err) - } - } -} diff --git a/e2e/v0/language/python_test.go b/e2e/v0/language/python_test.go deleted file mode 100644 index e1eca5249..000000000 --- a/e2e/v0/language/python_test.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2022 The envd 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 language - -import ( - . "github.com/onsi/ginkgo/v2" - - e2e "github.com/tensorchord/envd/e2e/v0" -) - -var _ = Describe("python", Ordered, func() { - It("Should build packages successfully", func() { - exampleName := "python/packages" - testcase := "e2e" - e := e2e.NewExample(e2e.BuildContextDirWithName(exampleName), testcase) - e.BuildImage(true)() - e.RunContainer()() - e.DestroyContainer()() - }) - It("Should build requirements successfully", func() { - exampleName := "python/requirements" - testcase := "e2e" - e := e2e.NewExample(e2e.BuildContextDirWithName(exampleName), testcase) - e.BuildImage(true)() - e.RunContainer()() - e.DestroyContainer()() - }) - It("Should build hybrid successfully", func() { - exampleName := "python/hybrid" - testcase := "e2e" - e := e2e.NewExample(e2e.BuildContextDirWithName(exampleName), testcase) - e.BuildImage(true)() - e.RunContainer()() - e.DestroyContainer()() - }) - - It("Should build conda with channel successfully", func() { - exampleName := "python/conda" - testcase := "e2e" - e := e2e.NewExample(e2e.BuildContextDirWithName(exampleName), testcase) - e.BuildImage(true)() - e.RunContainer()() - e.DestroyContainer()() - }) - - It("Should build conda with separate channel setting successfully", func() { - exampleName := "python/conda_channel" - testcase := "e2e" - e := e2e.NewExample(e2e.BuildContextDirWithName(exampleName), testcase) - e.BuildImage(true)() - e.RunContainer()() - e.DestroyContainer()() - }) -}) diff --git a/e2e/v0/language/testdata/python/conda/build.envd b/e2e/v0/language/testdata/python/conda/build.envd deleted file mode 100644 index acfeb96a0..000000000 --- a/e2e/v0/language/testdata/python/conda/build.envd +++ /dev/null @@ -1,3 +0,0 @@ -def build(): - install.conda_packages(env_file="env.yaml") - base(os="ubuntu20.04", language="python3.8") diff --git a/e2e/v0/language/testdata/python/conda/setup.py b/e2e/v0/language/testdata/python/conda/setup.py deleted file mode 100644 index 0be13a508..000000000 --- a/e2e/v0/language/testdata/python/conda/setup.py +++ /dev/null @@ -1,11 +0,0 @@ -from setuptools import setup, find_packages - -setup( - name="envd_e2e_conda_test", - version="1.0.0", - url="https://github.com/mypackage.git", - author="Author Name", - author_email="author@gmail.com", - description="Description of my package", - packages=find_packages(), -) diff --git a/e2e/v0/language/testdata/python/conda_channel/build.envd b/e2e/v0/language/testdata/python/conda_channel/build.envd deleted file mode 100644 index 46853559a..000000000 --- a/e2e/v0/language/testdata/python/conda_channel/build.envd +++ /dev/null @@ -1,10 +0,0 @@ -def build(): - config.conda_channel( - channel=""" -channels: - - defaults - - dglteam # dgllife in env.yaml only works if specify channels here - """ - ) - install.conda_packages(env_file="env.yaml") - base(os="ubuntu20.04", language="python3.8") diff --git a/e2e/v0/language/testdata/python/hybrid/build.envd b/e2e/v0/language/testdata/python/hybrid/build.envd deleted file mode 100644 index 5ed9ab562..000000000 --- a/e2e/v0/language/testdata/python/hybrid/build.envd +++ /dev/null @@ -1,2 +0,0 @@ -def build(): - install.python_packages(name=["via"], requirements="requirements.txt") diff --git a/e2e/v0/language/testdata/python/packages/build.envd b/e2e/v0/language/testdata/python/packages/build.envd deleted file mode 100644 index 3bb7a8e06..000000000 --- a/e2e/v0/language/testdata/python/packages/build.envd +++ /dev/null @@ -1,2 +0,0 @@ -def build(): - install.python_packages(name=["via"]) diff --git a/e2e/v0/language/testdata/python/requirements/build.envd b/e2e/v0/language/testdata/python/requirements/build.envd deleted file mode 100644 index 1a8b7799e..000000000 --- a/e2e/v0/language/testdata/python/requirements/build.envd +++ /dev/null @@ -1,2 +0,0 @@ -def build(): - install.python_packages(requirements="./requirements.txt") diff --git a/e2e/v0/language/testdata/run/build.envd b/e2e/v0/language/testdata/run/build.envd deleted file mode 100644 index 9ae7fa689..000000000 --- a/e2e/v0/language/testdata/run/build.envd +++ /dev/null @@ -1,11 +0,0 @@ -def build(): - base(os="ubuntu20.04", language="python3") - shell("zsh") - run( - commands=[ - "mkdir test", - "cd test", - "ls", - "pwd", - ] - ) diff --git a/e2e/v0/language/testdata/runtime/build.envd b/e2e/v0/language/testdata/runtime/build.envd deleted file mode 100644 index 1d2f67340..000000000 --- a/e2e/v0/language/testdata/runtime/build.envd +++ /dev/null @@ -1,14 +0,0 @@ -def build(): - base(os="ubuntu20.04", language="python3") - install.python_packages( - name=[ - "numpy", - ] - ) - shell("zsh") - runtime.command( - commands={ - "numpy": "python demo.py", - "root": "sudo ls /root", - } - ) diff --git a/e2e/v1/cli/bytecode_hash_test.go b/e2e/v1/cli/bytecode_hash_test.go deleted file mode 100644 index 527211d92..000000000 --- a/e2e/v1/cli/bytecode_hash_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2022 The envd 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 cli - -import ( - "context" - "os" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - e2e "github.com/tensorchord/envd/e2e/v1" -) - -func appendSomeToFile(path string) { - f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if err != nil { - panic(err) - } - blank := "\n\n" - _, err = f.Write([]byte(blank)) - if err != nil { - panic(err) - } - if err := f.Close(); err != nil { - panic(err) - } -} - -var _ = Describe("bytecode hash cache target", func() { - exampleName := "quick-start" - It("add some blank to build.envd", func() { - testcase := "add-blank" - e := e2e.NewExample(e2e.BuildContextDirWithName(exampleName), testcase) - ctx := context.TODO() - e.BuildImage(false)() - engine := e2e.GetEngine(ctx) - imageSum, err := engine.GetImage(ctx, e.Tag) - Expect(err).NotTo(HaveOccurred()) - oldCreated := imageSum.Created - appendSomeToFile("testdata/" + exampleName + "/build.envd") - e.BuildImage(false)() - imageSum, err = engine.GetImage(ctx, e.Tag) - Expect(err).NotTo(HaveOccurred()) - newCreated := imageSum.Created - Expect(oldCreated).To(Equal(newCreated)) - e.RemoveImage()() - }) -}) diff --git a/e2e/v1/cli/init_test.go b/e2e/v1/cli/init_test.go deleted file mode 100644 index 0a3772055..000000000 --- a/e2e/v1/cli/init_test.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2022 The envd 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 cli - -import ( - "os" - "path/filepath" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - e2e "github.com/tensorchord/envd/e2e/v1" - "github.com/tensorchord/envd/pkg/app" - "github.com/tensorchord/envd/pkg/home" - "github.com/tensorchord/envd/pkg/util/fileutil" -) - -var _ = Describe("init project", Ordered, func() { - var path string - BeforeAll(func() { - Expect(home.Initialize()).To(Succeed()) - envdApp := app.New() - err := envdApp.Run([]string{"envd.test", "--debug", "bootstrap"}) - Expect(err).To(Succeed()) - e2e.ResetEnvdApp() - path, err = os.MkdirTemp("", "envd_init_test_*") - Expect(err).To(Succeed()) - err = os.WriteFile(filepath.Join(path, "requirements.txt"), []byte("via"), 0666) - Expect(err).To(Succeed()) - }) - - It("init python env", func() { - envdApp := app.New() - err := envdApp.Run([]string{"envd.test", "--debug", "init", "-p", path}) - Expect(err).To(Succeed()) - exist, err := fileutil.FileExists(filepath.Join(path, "build.envd")) - Expect(err).To(Succeed()) - Expect(exist).To(BeTrue()) - }) - - Describe("run init env", Ordered, func() { - var e *e2e.Example - BeforeAll(func() { - // have to use `path` inside ginkgo closure - e = e2e.NewExample(path, "init_test") - e.RunContainer()() - }) - It("exec installed command inside container", func() { - _, err := e.Exec("via --help") - Expect(err).To(Succeed()) - e.DestroyContainer() - }) - }) - - AfterAll(func() { - os.RemoveAll(path) - }) -}) diff --git a/e2e/v1/cli/suite_test.go b/e2e/v1/cli/suite_test.go deleted file mode 100644 index d1c6671da..000000000 --- a/e2e/v1/cli/suite_test.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2022 The envd 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 cli - -import ( - "os" - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - "github.com/tensorchord/envd/pkg/version" -) - -func init() { - version.SetGitTagForE2ETest(os.Getenv("GIT_LATEST_TAG")) -} - -func TestMain(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "envd cli Suite") -} diff --git a/e2e/v1/cli/testdata/quick-start/demo.py b/e2e/v1/cli/testdata/quick-start/demo.py deleted file mode 100644 index 5a04cb44e..000000000 --- a/e2e/v1/cli/testdata/quick-start/demo.py +++ /dev/null @@ -1,5 +0,0 @@ -import numpy as np - -a = np.array([2, 3, 4]) - -print(a) diff --git a/e2e/v1/cli/up_test.go b/e2e/v1/cli/up_test.go deleted file mode 100644 index 70a358100..000000000 --- a/e2e/v1/cli/up_test.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2022 The envd 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 cli - -import ( - "context" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - e2e "github.com/tensorchord/envd/e2e/v1" - "github.com/tensorchord/envd/pkg/app" - "github.com/tensorchord/envd/pkg/driver/docker" - "github.com/tensorchord/envd/pkg/envd" - "github.com/tensorchord/envd/pkg/home" - "github.com/tensorchord/envd/pkg/types" -) - -var _ = Describe("up command", Ordered, func() { - buildContext := "testdata/up-test" - env := "up-test" - baseArgs := []string{ - "envd.test", "--debug", - } - BeforeAll(func() { - Expect(home.Initialize()).NotTo(HaveOccurred()) - envdApp := app.New() - err := envdApp.Run(append(baseArgs, "bootstrap")) - Expect(err).NotTo(HaveOccurred()) - _, err = docker.NewClient(context.TODO()) - Expect(err).NotTo(HaveOccurred()) - c := types.Context{Runner: types.RunnerTypeDocker} - opt := envd.Options{Context: &c} - envdEngine, err := envd.New(context.TODO(), opt) - Expect(err).NotTo(HaveOccurred()) - _, err = envdEngine.Destroy(context.TODO(), env) - Expect(err).NotTo(HaveOccurred()) - - }) - When("given the right arguments", func() { - It("should up and destroy successfully", func() { - args := append(baseArgs, []string{ - "up", "--path", buildContext, "--detach", "--force", - }...) - e2e.ResetEnvdApp() - envdApp := app.New() - err := envdApp.Run(args) - Expect(err).NotTo(HaveOccurred()) - - depsArgs := append(baseArgs, []string{ - "envs", "describe", "--env", env, - }...) - - err = envdApp.Run(depsArgs) - Expect(err).NotTo(HaveOccurred()) - - destroyArgs := append(baseArgs, []string{ - "destroy", "--path", buildContext, - }...) - err = envdApp.Run(destroyArgs) - Expect(err).NotTo(HaveOccurred()) - }) - }) -}) diff --git a/e2e/v1/docs/docs_test.go b/e2e/v1/docs/docs_test.go deleted file mode 100644 index 7defbd6f8..000000000 --- a/e2e/v1/docs/docs_test.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2022 The envd 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 docs - -import ( - "fmt" - "net/http" - "time" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - e2e "github.com/tensorchord/envd/e2e/v1" - "github.com/tensorchord/envd/pkg/app" - "github.com/tensorchord/envd/pkg/home" -) - -var _ = Describe("check examples in documentation", func() { - baseArgs := []string{ - "envd.test", "--debug", - } - - BeforeEach(func() { - Expect(home.Initialize()).NotTo(HaveOccurred()) - e2e.ResetEnvdApp() - envdApp := app.New() - err := envdApp.Run(append(baseArgs, "bootstrap")) - Expect(err).NotTo(HaveOccurred()) - }) - - It("can list envd envs", func() { - envdApp := app.New() - err := envdApp.Run([]string{"envd.test", "--debug", "envs", "list"}) - Expect(err).NotTo(HaveOccurred()) - }) - - It("can check envd envs details", func() { - buildContext := "testdata/minimal" - args := append(baseArgs, []string{ - "up", "--path", buildContext, "--detach", "--force", - }...) - e2e.ResetEnvdApp() - envdApp := app.New() - err := envdApp.Run(args) - Expect(err).NotTo(HaveOccurred()) - - err = envdApp.Run([]string{"envd.test", "--debug", "envs", "describe", "--env", "minimal"}) - Expect(err).NotTo(HaveOccurred()) - - destroyArgs := append(baseArgs, []string{ - "destroy", "--path", buildContext, - }...) - err = envdApp.Run(destroyArgs) - Expect(err).NotTo(HaveOccurred()) - }) - - upTests := []string{"testdata/minimal", "testdata/getting_started", "testdata/jupyter"} - - for _, v := range upTests { - It(fmt.Sprintf("can up %s environment", v), func() { - args := append(baseArgs, []string{ - "up", "--path", v, "-f", "build.envd", "--detach", "--force", - }...) - e2e.ResetEnvdApp() - envdApp := app.New() - err := envdApp.Run(args) - Expect(err).NotTo(HaveOccurred()) - - destroyArgs := append(baseArgs, []string{ - "destroy", "--path", v, - }...) - err = envdApp.Run(destroyArgs) - Expect(err).NotTo(HaveOccurred()) - }) - } - - It("should be able to use envdlib", func() { - path := "testdata/envdlib" - args := append(baseArgs, []string{ - "up", "--path", path, "-f", "build.envd", "--detach", "--force", - }...) - e2e.ResetEnvdApp() - envdApp := app.New() - err := envdApp.Run(args) - Expect(err).To(Succeed()) - - // check the port - time.Sleep(time.Second * 2) - resp, err := http.Get("http://127.0.0.1:8888") - Expect(err).To(Succeed()) - defer resp.Body.Close() - Expect(resp.StatusCode).To(Equal(200)) - - destroyArgs := append(baseArgs, []string{ - "destroy", "--path", path, - }...) - err = envdApp.Run(destroyArgs) - Expect(err).To(Succeed()) - }) -}) diff --git a/e2e/v1/docs/extra_lang_test.go b/e2e/v1/docs/extra_lang_test.go deleted file mode 100644 index 9ad186435..000000000 --- a/e2e/v1/docs/extra_lang_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package docs - -import ( - . "github.com/onsi/ginkgo/v2" - - e2e "github.com/tensorchord/envd/e2e/v1" -) - -var _ = Describe("lang", Ordered, func() { - It("Should build rlang environment successfully", func() { - exampleName := "rlang" - testcase := "e2e-extra" - e := e2e.NewExample(e2e.BuildContextDirWithName(exampleName), testcase) - e.BuildImage(true)() - e.RunContainer()() - e.DestroyContainer()() - }) - It("Should build Julia environment successfully", func() { - exampleName := "julia" - testcase := "e2e-extra" - e := e2e.NewExample(e2e.BuildContextDirWithName(exampleName), testcase) - e.BuildImage(true)() - e.RunContainer()() - e.DestroyContainer()() - }) -}) diff --git a/e2e/v1/language/testdata/python/conda/env.yaml b/e2e/v1/language/testdata/python/conda/env.yaml deleted file mode 100644 index 329719e5e..000000000 --- a/e2e/v1/language/testdata/python/conda/env.yaml +++ /dev/null @@ -1,9 +0,0 @@ -name: imarobot # should ignore this name -channels: - - conda-forge - - defaults -dependencies: - - babel - - pip: - - via - - -e . \ No newline at end of file diff --git a/e2e/v1/language/testdata/python/conda_channel/env.yaml b/e2e/v1/language/testdata/python/conda_channel/env.yaml deleted file mode 100644 index 849610dea..000000000 --- a/e2e/v1/language/testdata/python/conda_channel/env.yaml +++ /dev/null @@ -1,3 +0,0 @@ -name: imarobot # should ignore this name -dependencies: - - dgl \ No newline at end of file diff --git a/e2e/v1/language/testdata/python/hybrid/requirements.txt b/e2e/v1/language/testdata/python/hybrid/requirements.txt deleted file mode 100644 index 682070c44..000000000 --- a/e2e/v1/language/testdata/python/hybrid/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -via diff --git a/e2e/v1/language/testdata/python/requirements/requirements.txt b/e2e/v1/language/testdata/python/requirements/requirements.txt deleted file mode 100644 index ca015edcd..000000000 --- a/e2e/v1/language/testdata/python/requirements/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -via --e . \ No newline at end of file diff --git a/e2e/v1/language/testdata/python/requirements/setup.py b/e2e/v1/language/testdata/python/requirements/setup.py deleted file mode 100644 index 0be13a508..000000000 --- a/e2e/v1/language/testdata/python/requirements/setup.py +++ /dev/null @@ -1,11 +0,0 @@ -from setuptools import setup, find_packages - -setup( - name="envd_e2e_conda_test", - version="1.0.0", - url="https://github.com/mypackage.git", - author="Author Name", - author_email="author@gmail.com", - description="Description of my package", - packages=find_packages(), -) diff --git a/e2e/v1/language/testdata/runtime/demo.py b/e2e/v1/language/testdata/runtime/demo.py deleted file mode 100644 index 5a04cb44e..000000000 --- a/e2e/v1/language/testdata/runtime/demo.py +++ /dev/null @@ -1,5 +0,0 @@ -import numpy as np - -a = np.array([2, 3, 4]) - -print(a) diff --git a/envd/api/v0/__init__.py b/envd/api/v0/__init__.py index 422fde531..977619ba6 100644 --- a/envd/api/v0/__init__.py +++ b/envd/api/v0/__init__.py @@ -19,6 +19,12 @@ in [tensorchord/envd](https://github.com/tensorchord/envd/tree/main/envd/api) repo. Please update the python file there instead of directly editing file inside envd-docs repo. ::: + +::: warning +v0 syntax is no longer supported from `envd>=v1.0`. Check the [upgrade guide](https://envd.tensorchord.ai/guide/v1.html). + +If you want to use v0 syntax, try `pip install 'envd<1'`. +::: """ from typing import List, Optional @@ -28,9 +34,9 @@ def base(os: str, language: str, image: Optional[str]): """Set base image Args: - os (str): The operating system (i.e. `ubuntu20.04`) - language (str): The programing language dependency (i.e. `python3.8`) - image (Optional[str]): Custom image (i.e. `python:3.9-slim`) + os (str): The operating system (i.e. `ubuntu22.04`) + language (str): The programming language dependency (i.e. `python3.8`) + image (Optional[str]): Custom image (i.e. `python:3.11-slim`) """ @@ -51,7 +57,7 @@ def run(commands: List[str], mount_host: bool = False): Enabling this will disable the build cache for this operation. Example: - ``` + ```python run(commands=["conda install -y -c conda-forge exa"]) ``` """ @@ -70,7 +76,7 @@ def git_config( editor (optional, str): Editor for git operations Example usage: - ``` + ```python git_config(name="My Name", email="my@email.com", editor="vim") ``` """ @@ -80,17 +86,17 @@ def include(git: str): """Import from another git repo This will pull the git repo and execute all the `envd` files. The return value will be a module - contains all the variables/functions defined (expect those has `_` prefix). + contains all the variables/functions defined (except the ones with `_` prefix). Args: git (str): git URL Example usage: - ``` + ```python envd = include("https://github.com/tensorchord/envdlib") def build(): - base(os="ubuntu20.04", language="python") + base(os="ubuntu22.04", language="python") envd.tensorboard(8000) ``` """ diff --git a/envd/api/v0/config.py b/envd/api/v0/config.py index aaa9fc763..a3aa9339c 100644 --- a/envd/api/v0/config.py +++ b/envd/api/v0/config.py @@ -19,26 +19,32 @@ in [tensorchord/envd](https://github.com/tensorchord/envd/tree/main/envd/api) repo. Please update the python file there instead of directly editing file inside envd-docs repo. ::: + +::: warning +v0 syntax is no longer supported from `envd>=v1.0`. Check the [upgrade guide](https://envd.tensorchord.ai/guide/v1.html). + +If you want to use v0 syntax, try `pip install 'envd<1'`. +::: """ -from typing import Optional, List +from typing import List, Optional def apt_source(source: Optional[str]): """Configure apt sources Example usage: - ``` + ```python apt_source(source=''' - deb https://mirror.sjtu.edu.cn/ubuntu focal main restricted - deb https://mirror.sjtu.edu.cn/ubuntu focal-updates main restricted - deb https://mirror.sjtu.edu.cn/ubuntu focal universe - deb https://mirror.sjtu.edu.cn/ubuntu focal-updates universe - deb https://mirror.sjtu.edu.cn/ubuntu focal multiverse - deb https://mirror.sjtu.edu.cn/ubuntu focal-updates multiverse - deb https://mirror.sjtu.edu.cn/ubuntu focal-backports main restricted universe multiverse - deb http://archive.canonical.com/ubuntu focal partner - deb https://mirror.sjtu.edu.cn/ubuntu focal-security main restricted universe multiverse + deb https://mirror.sjtu.edu.cn/ubuntu jammy main restricted + deb https://mirror.sjtu.edu.cn/ubuntu jammy-updates main restricted + deb https://mirror.sjtu.edu.cn/ubuntu jammy universe + deb https://mirror.sjtu.edu.cn/ubuntu jammy-updates universe + deb https://mirror.sjtu.edu.cn/ubuntu jammy multiverse + deb https://mirror.sjtu.edu.cn/ubuntu jammy-updates multiverse + deb https://mirror.sjtu.edu.cn/ubuntu jammy-backports main restricted universe multiverse + deb http://archive.canonical.com/ubuntu jammy partner + deb https://mirror.sjtu.edu.cn/ubuntu jammy-security main restricted universe multiverse ''') ``` @@ -71,7 +77,7 @@ def conda_channel(channel: str): """Configure conda channel mirror Example usage: - ``` + ```python config.conda_channel(channel=''' channels: - defaults @@ -94,7 +100,7 @@ def entrypoint(args: List[str]): """Configure entrypoint for custom base image Example usage: - ``` + ```python config.entrypoint(["date", "-u"]) ``` @@ -107,7 +113,7 @@ def gpu(count: int): """Configure the number of GPUs required Example usage: - ``` + ```python config.gpu(count=2) ``` @@ -116,6 +122,19 @@ def gpu(count: int): """ +def shm_size(size: int): + """Configure the shared memory size (megabyte) of docker containers + + Example usage: + ```python + config.shm_size(size=1024) + ``` + + Args: + size (int): the shared memory size (megabyte) of docker containers + """ + + def cran_mirror(url: str): """Configure the mirror URL, default is https://cran.rstudio.com @@ -135,7 +154,7 @@ def julia_pkg_server(url: str): def rstudio_server(): """ - Enable the RStudio Server (only work for `base(os="ubuntu20.04", language="r")`) + Enable the RStudio Server (only work for `base(os="ubuntu22.04", language="r")`) """ diff --git a/envd/api/v0/install.py b/envd/api/v0/install.py index a5c6929cb..a9ab7cab7 100644 --- a/envd/api/v0/install.py +++ b/envd/api/v0/install.py @@ -19,13 +19,19 @@ in [tensorchord/envd](https://github.com/tensorchord/envd/tree/main/envd/api) repo. Please update the python file there instead of directly editing file inside envd-docs repo. ::: + +::: warning +v0 syntax is no longer supported from `envd>=v1.0`. Check the [upgrade guide](https://envd.tensorchord.ai/guide/v1.html). + +If you want to use v0 syntax, try `pip install 'envd<1'`. +::: """ from typing import List, Optional def apt_packages(name: List[str]): - """Install package by system-level package manager (apt on Ubuntu) + """Install package using the system package manager (apt on Ubuntu) Args: name (List[str]): apt package name list diff --git a/envd/api/v0/io.py b/envd/api/v0/io.py index fd47b73cf..9c40889e6 100644 --- a/envd/api/v0/io.py +++ b/envd/api/v0/io.py @@ -19,8 +19,13 @@ in [tensorchord/envd](https://github.com/tensorchord/envd/tree/main/envd/api) repo. Please update the python file there instead of directly editing file inside envd-docs repo. ::: -""" +::: warning +v0 syntax is no longer supported from `envd>=v1.0`. Check the [upgrade guide](https://envd.tensorchord.ai/guide/v1.html). + +If you want to use v0 syntax, try `pip install 'envd<1'`. +::: +""" from typing import Optional diff --git a/envd/api/v0/runtime.py b/envd/api/v0/runtime.py index 4a6769c44..c979536e4 100644 --- a/envd/api/v0/runtime.py +++ b/envd/api/v0/runtime.py @@ -19,9 +19,15 @@ in [tensorchord/envd](https://github.com/tensorchord/envd/tree/main/envd/api) repo. Please update the python file there instead of directly editing file inside envd-docs repo. ::: + +::: warning +v0 syntax is no longer supported from `envd>=v1.0`. Check the [upgrade guide](https://envd.tensorchord.ai/guide/v1.html). + +If you want to use v0 syntax, try `pip install 'envd<1'`. +::: """ -from typing import Dict, Optional, List +from typing import Dict, List, Optional def command(commands: Dict[str, str]): @@ -31,7 +37,7 @@ def command(commands: Dict[str, str]): commands (Dict[str, str]): map name to command, similar to Makefile Example usage: - ``` + ```python runtime.command(commands={ "train": "python train.py --epoch 20 --notify me@tensorchord.ai", "run": "python server.py --batch 1 --host 0.0.0.0 --port 8000", @@ -43,8 +49,8 @@ def command(commands: Dict[str, str]): def expose( - envd_port: str, - host_port: Optional[str], + envd_port: int, + host_port: Optional[int], service: Optional[str], listen_addr: Optional[str], ): @@ -52,8 +58,8 @@ def expose( Proposal: https://github.com/tensorchord/envd/pull/780 Args: - envd_port (str): port in `envd` container - host_port (Optional[str]): port in the host, if not provided or + envd_port (int): port in `envd` container + host_port (Optional[int]): port in the host, if not provided or `host_port=0`, `envd` will randomly choose a free port service (Optional[str]): service name listen_addr (Optional[str]): address to listen on @@ -64,7 +70,7 @@ def daemon(commands: List[List[str]]): """Run daemon processes in the container Proposal: https://github.com/tensorchord/envd/pull/769 - It's better to redirect the logs to local files for debug purposes. + It's better to redirect the logs to local files for debugging purposes. You can find the generated horust config files under `/etc/horust/services` and log files under `/var/log/horust` in the container. @@ -73,7 +79,7 @@ def daemon(commands: List[List[str]]): commands (List[List[str]]): run multiple commands in the background Example usage: - ``` + ```python runtime.daemon(commands=[ ["jupyter-lab", "--port", "8080"], ["python3", "serving.py", ">>serving.log", "2>&1"], @@ -90,7 +96,7 @@ def environ(env: Dict[str, str], extra_path: List[str]): extra_path (List[str]): additional PATH Example usage: - ``` + ```python runtime.environ(env={"ENVD_MODE": "DEV"}, extra_path=["/usr/bin/go/bin"]) ``` """ diff --git a/envd/api/v1/__init__.py b/envd/api/v1/__init__.py index aac464e60..96e7aef16 100644 --- a/envd/api/v1/__init__.py +++ b/envd/api/v1/__init__.py @@ -19,12 +19,6 @@ in [tensorchord/envd](https://github.com/tensorchord/envd/tree/main/envd/api) repo. Please update the python file there instead of directly editing file inside envd-docs repo. ::: - -::: warning -Enable v1 by adding `# syntax=v1` to the 1st line of your envd file. - -v1 is experimental and may change in the future. Make sure to freeze the envd version for online CI/CD. -::: """ from typing import List, Optional @@ -33,21 +27,21 @@ """ -def base(image: str = "ubuntu:20.04", dev: bool = False): +def base(image: str = "ubuntu:22.04", dev: bool = False): """Set up the base env. Args: - image (str): docker image, can be any Debian-based images + image (str): docker image, can be any Debian-based image dev (bool): enabling the dev env will add lots of development related libraries like envd-sshd, vim, git, shell prompt, vscode extensions, etc. """ -def shell(name: str = "base"): +def shell(name: str = "bash"): """Interactive shell Args: - name (str): shell name (i.e. `zsh`, `bash`) + name (str): shell name (i.e. `zsh`, `bash`, `fish`) """ @@ -60,7 +54,7 @@ def run(commands: List[str], mount_host: bool = False): Enabling this will disable the build cache for this operation. Example: - ``` + ```python run(commands=["conda install -y -c conda-forge exa"]) ``` """ @@ -71,7 +65,7 @@ def git_config( email: Optional[str] = None, editor: Optional[str] = None, ): - """Setup git config + """Setup git config. Args: name (str): User name @@ -79,7 +73,7 @@ def git_config( editor (str): Editor for git operations Example usage: - ``` + ```python git_config(name="My Name", email="my@email.com", editor="vim") ``` """ @@ -89,17 +83,17 @@ def include(git: str): """Import from another git repo This will pull the git repo and execute all the `envd` files. The return value will be a module - contains all the variables/functions defined (expect those has `_` prefix). + contains all the variables/functions defined (except the ones with `_` prefix). Args: git (str): git URL Example usage: - ``` + ```python envd = include("https://github.com/tensorchord/envdlib") def build(): - base(os="ubuntu20.04", language="python") + base(os="ubuntu22.04", language="python") envd.tensorboard(host_port=8000) ``` """ diff --git a/envd/api/v1/config.py b/envd/api/v1/config.py index 43a18a477..478e82972 100644 --- a/envd/api/v1/config.py +++ b/envd/api/v1/config.py @@ -19,12 +19,6 @@ in [tensorchord/envd](https://github.com/tensorchord/envd/tree/main/envd/api) repo. Please update the python file there instead of directly editing file inside envd-docs repo. ::: - -::: warning -Enable v1 by adding `# syntax=v1` to the 1st line of your envd file. - -v1 is experimental and may change in the future. Make sure to freeze the envd version for online CI/CD. -::: """ from typing import List, Optional @@ -34,17 +28,17 @@ def apt_source(source: Optional[str]): """Configure apt sources Example usage: - ``` + ```python apt_source(source=''' - deb https://mirror.sjtu.edu.cn/ubuntu focal main restricted - deb https://mirror.sjtu.edu.cn/ubuntu focal-updates main restricted - deb https://mirror.sjtu.edu.cn/ubuntu focal universe - deb https://mirror.sjtu.edu.cn/ubuntu focal-updates universe - deb https://mirror.sjtu.edu.cn/ubuntu focal multiverse - deb https://mirror.sjtu.edu.cn/ubuntu focal-updates multiverse - deb https://mirror.sjtu.edu.cn/ubuntu focal-backports main restricted universe multiverse - deb http://archive.canonical.com/ubuntu focal partner - deb https://mirror.sjtu.edu.cn/ubuntu focal-security main restricted universe multiverse + deb https://mirror.sjtu.edu.cn/ubuntu jammy main restricted + deb https://mirror.sjtu.edu.cn/ubuntu jammy-updates main restricted + deb https://mirror.sjtu.edu.cn/ubuntu jammy universe + deb https://mirror.sjtu.edu.cn/ubuntu jammy-updates universe + deb https://mirror.sjtu.edu.cn/ubuntu jammy multiverse + deb https://mirror.sjtu.edu.cn/ubuntu jammy-updates multiverse + deb https://mirror.sjtu.edu.cn/ubuntu jammy-backports main restricted universe multiverse + deb http://archive.canonical.com/ubuntu jammy partner + deb https://mirror.sjtu.edu.cn/ubuntu jammy-security main restricted universe multiverse ''') ``` @@ -77,7 +71,7 @@ def conda_channel(channel: str): """Configure conda channel mirror Example usage: - ``` + ```python config.conda_channel(channel=''' channels: - defaults @@ -100,7 +94,7 @@ def entrypoint(args: List[str]): """Configure entrypoint for custom base image Example usage: - ``` + ```python config.entrypoint(["date", "-u"]) ``` @@ -113,7 +107,7 @@ def gpu(count: int): """Configure the number of GPUs required Example usage: - ``` + ```python config.gpu(count=2) ``` @@ -122,6 +116,19 @@ def gpu(count: int): """ +def shm_size(size: int): + """Configure the shared memory size (megabyte) of docker containers + + Example usage: + ```python + config.shm_size(size=1024) + ``` + + Args: + size (int): the shared memory size (megabyte) of docker containers + """ + + def cran_mirror(url: str): """Configure the mirror URL, default is https://cran.rstudio.com @@ -141,7 +148,7 @@ def julia_pkg_server(url: str): def rstudio_server(): """ - Enable the RStudio Server (only work for `base(os="ubuntu20.04", language="r")`) + Enable the RStudio Server (only work for `base(os="ubuntu22.04", language="r")`) """ @@ -155,7 +162,7 @@ def repo(url: str, description: str): def owner(uid: int, gid: int): - """Configure uid:gid as the environmen owner. + """Configure uid:gid as the environment owner. This can also be achieved by using flag `envd --owner uid:gid build` or environment variable `ENVD_BUILD_OWNER=uid:gid envd build` diff --git a/envd/api/v1/install.py b/envd/api/v1/install.py index 792f1ff06..36261902a 100644 --- a/envd/api/v1/install.py +++ b/envd/api/v1/install.py @@ -19,18 +19,12 @@ in [tensorchord/envd](https://github.com/tensorchord/envd/tree/main/envd/api) repo. Please update the python file there instead of directly editing file inside envd-docs repo. ::: - -::: warning -Enable v1 by adding `# syntax=v1` to the 1st line of your envd file. - -v1 is experimental and may change in the future. Make sure to freeze the envd version for online CI/CD. -::: """ -from typing import List, Optional +from typing import Optional, Sequence -def python(version: str = "3.9"): +def python(version: str = "3.11"): """Install python. If `install.conda` is not used, this will create a solo Python environment. Otherwise, it @@ -49,6 +43,37 @@ def conda(use_mamba: bool = False): """ +def pixi(use_pixi_mirror: bool = False, pypi_index: Optional[str] = None): + """Install Pixi (https://github.com/prefix-dev/pixi). + + `pixi` is an alternative to `conda` that is written in Rust and provides faster + dependency resolution and installation. It also simplify the project management. + + This doesn't support installing Python packages through `install.python_packages` + because that part should be managed by `pixi`. You can run `pixi shell` in the + `envd` environment to sync all the dependencies. + + Args: + use_pixi_mirror (bool): use pixi mirror + pypi_index (Optional[str]): customize pypi index url + """ + + +def uv(python_version: str = "3.11"): + """Install UV (an extremely fast Python package and project manager). + + `uv` is much faster than `conda`. Choose this one instead of `conda` if you don't + need any machine learning packages. + + This doesn't support installing Python packages through `install.python_packages` + because that part should be managed by `uv`. You can run `uv sync` in the `envd` + environment to install all the dependencies. + + Args: + python_version (str): install this Python version through UV + """ + + def r_lang(): """Install R Lang.""" @@ -57,66 +82,77 @@ def julia(): """Install Julia.""" -def apt_packages(name: List[str] = []): - """Install package by system-level package manager (apt on Ubuntu). +def apt_packages(name: Sequence[str] = ()): + """Install package using the system package manager (apt on Ubuntu). Args: - name (List[str]): apt package name list + name (Sequence[str]): apt package name list """ def python_packages( - name: List[str] = [], requirements: str = "", local_wheels: List[str] = [] + name: Sequence[str] = (), requirements: str = "", local_wheels: Sequence[str] = () ): """Install python package by pip. Args: - name (List[str]): package name list + name (Sequence[str]): package name list requirements (str): requirements file path - local_wheels (List[str]): local wheels + local_wheels (Sequence[str]): local wheels (wheel files should be placed under the current directory) """ -def conda_packages(name: List[str] = [], channel: List[str] = [], env_file: str = ""): +def conda_packages( + name: Sequence[str] = (), channel: Sequence[str] = (), env_file: str = "" +): """Install python package by Conda Args: - name (List[str]): List of package names with optional version assignment, + name (Sequence[str]): List of package names with optional version assignment, such as ['pytorch', 'tensorflow==1.13.0'] - channel (List[str]): additional channels + channel (Sequence[str]): additional channels env_file (str): conda env file path """ -def r_packages(name: List[str]): +def r_packages(name: Sequence[str]): """Install R packages by R package manager. Args: - name (List[str]): package name list + name (Sequence[str]): package name list """ -def julia_packages(name: List[str]): +def julia_packages(name: Sequence[str]): """Install Julia packages. Args: - name (List[str]): List of Julia packages + name (Sequence[str]): List of Julia packages """ -def vscode_extensions(name: List[str]): +def vscode_extensions(name: Sequence[str]): """Install VS Code extensions Args: - name (List[str]): extension names, such as ['ms-python.python'] + name (Sequence[str]): extension names, such as ['ms-python.python'] """ def cuda(version: str, cudnn: Optional[str] = "8"): - """Install CUDA dependency + """Replace the base image with a `nvidia/cuda` image. + + This will replace the default base image to an `nvidia/cuda` image. You can + also use a CUDA base image directly like + `base(image="nvidia/cuda:12.2.0-devel-ubuntu22.04", dev=True)`. Args: version (str): CUDA version, such as '11.6.2' cudnn (optional, str): CUDNN version, such as '8' + + Example usage: + ```python + install.cuda(version="11.6.2", cudnn="8") + ``` """ diff --git a/envd/api/v1/io.py b/envd/api/v1/io.py index d33744364..0b3fb5b46 100644 --- a/envd/api/v1/io.py +++ b/envd/api/v1/io.py @@ -19,23 +19,26 @@ in [tensorchord/envd](https://github.com/tensorchord/envd/tree/main/envd/api) repo. Please update the python file there instead of directly editing file inside envd-docs repo. ::: - -::: warning -Enable v1 by adding `# syntax=v1` to the 1st line of your envd file. - -v1 is experimental and may change in the future. Make sure to freeze the envd version for online CI/CD. -::: """ from typing import Optional -def copy(host_path: str, envd_path: str): +def copy(source: str, target: str, image: Optional[str]): """Copy from host path to container path (build time) Args: - host_path (str): source path in the host machine - envd_path (str): destination path in the envd container + source (str): source path in the host machine or in the ``image`` + target (str): destination path in the envd container + image(Optional[str]): image name, if not specified, will use the host + + Examples: + ```python + # copy from host to container + io.copy(source='main.py', target='/home/envd/') + # copy from image to container + io.copy(source='/bin/micromamba', target='/usr/local/bin/micromamba', image='mambaorg/micromamba:1.0.0') + ``` """ diff --git a/envd/api/v1/runtime.py b/envd/api/v1/runtime.py index d1f20115e..081ab2bcd 100644 --- a/envd/api/v1/runtime.py +++ b/envd/api/v1/runtime.py @@ -19,15 +19,9 @@ in [tensorchord/envd](https://github.com/tensorchord/envd/tree/main/envd/api) repo. Please update the python file there instead of directly editing file inside envd-docs repo. ::: - -::: warning -Enable v1 by adding `# syntax=v1` to the 1st line of your envd file. - -v1 is experimental and may change in the future. Make sure to freeze the envd version for online CI/CD. -::: """ -from typing import List, Optional, Dict +from typing import Dict, List, Optional def command(commands: Dict[str, str]): @@ -37,7 +31,7 @@ def command(commands: Dict[str, str]): commands (Dict[str, str]): map name to command, similar to Makefile Example usage: - ``` + ```python runtime.command(commands={ "train": "python train.py --epoch 20 --notify me@tensorchord.ai", "run": "python server.py --batch 1 --host 0.0.0.0 --port 8000", @@ -49,8 +43,8 @@ def command(commands: Dict[str, str]): def expose( - envd_port: str, - host_port: Optional[str], + envd_port: int, + host_port: Optional[int], service: Optional[str], listen_addr: Optional[str], ): @@ -58,8 +52,8 @@ def expose( Proposal: https://github.com/tensorchord/envd/pull/780 Args: - envd_port (str): port in `envd` container - host_port (Optional[str]): port in the host, if not provided or + envd_port (int): port in `envd` container + host_port (Optional[int]): port in the host, if not provided or `host_port=0`, `envd` will randomly choose a free port service (Optional[str]): service name listen_addr (Optional[str]): address to listen on @@ -70,7 +64,7 @@ def daemon(commands: List[List[str]]): """Run daemon processes in the container Proposal: https://github.com/tensorchord/envd/pull/769 - It's better to redirect the logs to local files for debug purposes. + It's better to redirect the logs to local files for debugging purposes. You can find the generated horust config files under `/etc/horust/services` and log files under `/var/log/horust` in the container. @@ -79,7 +73,7 @@ def daemon(commands: List[List[str]]): commands (List[List[str]]): run multiple commands in the background Example usage: - ``` + ```python runtime.daemon(commands=[ ["jupyter-lab", "--port", "8080"], ["python3", "serving.py", ">>serving.log", "2>&1"], @@ -96,7 +90,7 @@ def environ(env: Dict[str, str], extra_path: List[str]): extra_path (List[str]): additional PATH Example usage: - ``` + ```python runtime.environ(env={"ENVD_MODE": "DEV"}, extra_path=["/usr/bin/go/bin"]) ``` """ diff --git a/examples/conda/build.envd b/examples/conda/build.envd index 41c73fe27..39adebd10 100644 --- a/examples/conda/build.envd +++ b/examples/conda/build.envd @@ -27,6 +27,8 @@ custom_channels: ], channel=["pytorch"], ) - base(os="ubuntu20.04", language="python3.8") + base(dev=True) + install.conda() + install.python() install.python_packages(name=["flask"]) install.cuda(version="11.2.2", cudnn="8") diff --git a/examples/custom-image/build.envd b/examples/custom-image/build.envd index 6c295247e..b233d2e8c 100644 --- a/examples/custom-image/build.envd +++ b/examples/custom-image/build.envd @@ -1,5 +1,5 @@ def build(): - base(language="python", image="python:3.9-slim") + base(image="python:3.11-slim", dev=True) install.python_packages( name=[ "via", diff --git a/examples/dgl/build.envd b/examples/dgl/build.envd index b1fb43487..8e1f08e8a 100644 --- a/examples/dgl/build.envd +++ b/examples/dgl/build.envd @@ -1,6 +1,8 @@ def build(): - # Use ubuntu20.04 as base image and install python - base(os="ubuntu20.04", language="python3") + # Use ubuntu:22.04 as base image and install python + base(image="ubuntu:22.04", dev=True) + install.conda() + install.python() # Add the packages you are using here install.python_packages(["numpy", "dgl", "torch"]) @@ -13,8 +15,10 @@ def build(): def build_gpu(): - # Use ubuntu20.04 as base image and install python - base(os="ubuntu20.04", language="python3") + # Use ubuntu:22.04 as base image and install python + base(dev=True) + install.conda() + install.python() # install cuda install.cuda(version="11.2.2", cudnn="8") diff --git a/examples/dgl/train.py b/examples/dgl/train.py index d707c3c4f..f60326f42 100644 --- a/examples/dgl/train.py +++ b/examples/dgl/train.py @@ -13,13 +13,14 @@ # See the License for the specific language governing permissions and # limitations under the License. +import argparse + +import dgl.nn as dglnn import torch -import torch.nn as nn import torch.nn.functional as F -import dgl.nn as dglnn -from dgl.data import CoraGraphDataset, CiteseerGraphDataset, PubmedGraphDataset from dgl import AddSelfLoop -import argparse +from dgl.data import CiteseerGraphDataset, CoraGraphDataset, PubmedGraphDataset +from torch import nn class GAT(nn.Module): @@ -52,10 +53,7 @@ def forward(self, g, inputs): h = inputs for i, layer in enumerate(self.gat_layers): h = layer(g, h) - if i == 1: # last layer - h = h.mean(1) - else: # other layer(s) - h = h.flatten(1) + h = h.mean(1) if i == 1 else h.flatten(1) return h @@ -102,7 +100,7 @@ def train(g, features, labels, masks, model): help="Dataset name ('cora', 'citeseer', 'pubmed').", ) args = parser.parse_args() - print(f"Training with DGL built-in GATConv module.") + print("Training with DGL built-in GATConv module.") # load and preprocess dataset transform = ( diff --git a/examples/download_files/build.envd b/examples/download_files/build.envd index 1a59cf8c2..2777ed669 100644 --- a/examples/download_files/build.envd +++ b/examples/download_files/build.envd @@ -1,5 +1,7 @@ def build(): - base(os="ubuntu20.04", language="python") + base(dev=True) + install.conda() + install.python() io.http( url="https://github.com/tensorchord/envd/releases/download/v0.2.0-alpha.18/envd-ssh_0.2.0-alpha.18_Linux_x86_64", checksum="sha256:163fa5d9775a3666ec91e2422a794277cc8575147f15767d364cc40157888cfb", diff --git a/examples/dpgen2/build.envd b/examples/dpgen2/build.envd index 904cf80d9..f32f1683a 100644 --- a/examples/dpgen2/build.envd +++ b/examples/dpgen2/build.envd @@ -23,7 +23,9 @@ def install_kubectl_kind(env_name): def build(): - base(os="ubuntu20.04", language="python3.9") + base(dev=True) + install.conda() + install.python() install.python_packages( name=[ "pydflow", diff --git a/examples/ianvs/build.envd b/examples/ianvs/build.envd index efd9206f4..22a291daf 100644 --- a/examples/ianvs/build.envd +++ b/examples/ianvs/build.envd @@ -1,5 +1,7 @@ def build(): - base(os="ubuntu20.04", language="python3.6") + base(dev=True) + install.conda() + install.python() shell("zsh") install.apt_packages(name=["git", "libgl1-mesa-glx", "zip"]) run( diff --git a/examples/include_pkg/build.envd b/examples/include_pkg/build.envd index dd5a357ba..f0e2e112a 100644 --- a/examples/include_pkg/build.envd +++ b/examples/include_pkg/build.envd @@ -2,5 +2,7 @@ envdlib = include("https://github.com/tensorchord/envdlib") def build(): - base(os="ubuntu20.04", language="python") + base(dev=True) + install.conda() + install.python() envdlib.tensorboard(8888) diff --git a/examples/julia-basic/build.envd b/examples/julia-basic/build.envd index 712dbd641..bd62d2afe 100644 --- a/examples/julia-basic/build.envd +++ b/examples/julia-basic/build.envd @@ -1,5 +1,6 @@ def build(): - base(os="ubuntu20.04", language="julia") + base(dev=True) + install.julia() # config.julia_pkg_server(url="https://mirrors.tuna.tsinghua.edu.cn/julia") install.julia_packages(["Example"]) shell("zsh") diff --git a/examples/llm-inference/8bit.py b/examples/llm-inference/8bit.py index 03c3f6978..380f052f4 100644 --- a/examples/llm-inference/8bit.py +++ b/examples/llm-inference/8bit.py @@ -1,5 +1,4 @@ -from transformers import AutoModelForCausalLM, AutoTokenizer, set_seed -from transformers import pipeline +from transformers import AutoModelForCausalLM, AutoTokenizer name = "bigscience/bloom-3b" text = "Hello my name is" diff --git a/examples/llm-inference/build.envd b/examples/llm-inference/build.envd index 66c62568b..e4dfc342e 100644 --- a/examples/llm-inference/build.envd +++ b/examples/llm-inference/build.envd @@ -1,6 +1,4 @@ -# syntax=v1 def build(): - # base(os="ubuntu20.04", language="python3") base(dev=True) install.conda() install.python() diff --git a/examples/llm-inference/main.py b/examples/llm-inference/main.py index b7435ab1e..ee723d626 100644 --- a/examples/llm-inference/main.py +++ b/examples/llm-inference/main.py @@ -1,4 +1,3 @@ -from transformers import AutoModelForCausalLM, AutoTokenizer, set_seed from transformers import pipeline name = "bigscience/bloom-3b" diff --git a/examples/mnist/build.envd b/examples/mnist/build.envd index 5bdaf6d2a..571e5d6ea 100644 --- a/examples/mnist/build.envd +++ b/examples/mnist/build.envd @@ -1,5 +1,7 @@ def base_env(): - base(os="ubuntu20.04", language="python3") + base(dev=True) + install.conda() + install.python() install.vscode_extensions( [ "ms-python.python", diff --git a/examples/mnist/main.py b/examples/mnist/main.py index c462f5224..471ca5d76 100644 --- a/examples/mnist/main.py +++ b/examples/mnist/main.py @@ -6,13 +6,14 @@ # from https://github.com/pytorch/examples/blob/main/mnist/main.py from __future__ import print_function + import argparse + import torch -import torch.nn as nn import torch.nn.functional as F -import torch.optim as optim -from torchvision import datasets, transforms +from torch import nn, optim from torch.optim.lr_scheduler import StepLR +from torchvision import datasets, transforms class Net(nn.Module): diff --git a/examples/mnist/mnist.ipynb b/examples/mnist/mnist.ipynb index 189cd9e56..b2f819d91 100644 --- a/examples/mnist/mnist.ipynb +++ b/examples/mnist/mnist.ipynb @@ -8,13 +8,12 @@ "outputs": [], "source": [ "from __future__ import print_function\n", - "import argparse\n", + "\n", "import torch\n", - "import torch.nn as nn\n", "import torch.nn.functional as F\n", - "import torch.optim as optim\n", - "from torchvision import datasets, transforms\n", + "from torch import nn, optim\n", "from torch.optim.lr_scheduler import StepLR\n", + "from torchvision import datasets, transforms\n", "\n", "\n", "class Net(nn.Module):\n", diff --git a/examples/python-basic/build.envd b/examples/python-basic/build.envd index a006b104a..94705c65f 100644 --- a/examples/python-basic/build.envd +++ b/examples/python-basic/build.envd @@ -1,12 +1,14 @@ def build(): - base(os="ubuntu20.04", language="python") + base(dev=True) + install.conda() + install.python() # config.pip_index(url = "https://pypi.tuna.tsinghua.edu.cn/simple") install.python_packages( [ "via", ] ) - io.copy(host_path="./build.envd", envd_path="/") + io.copy("./build.envd", "/") runtime.command( commands={ "test": "ls /", diff --git a/examples/pytorch-profiler/build.envd b/examples/pytorch-profiler/build.envd index d7d36570b..5d55d3bbe 100644 --- a/examples/pytorch-profiler/build.envd +++ b/examples/pytorch-profiler/build.envd @@ -2,7 +2,9 @@ envdlib = include("https://github.com/tensorchord/envdlib") def build(): - base(os="ubuntu20.04", language="python3") + base(dev=True) + install.conda() + install.python() shell("zsh") install.cuda(version="11.2.2", cudnn="8") install.python_packages( diff --git a/examples/pytorch-profiler/main.py b/examples/pytorch-profiler/main.py index 1fca3e609..873e15b06 100644 --- a/examples/pytorch-profiler/main.py +++ b/examples/pytorch-profiler/main.py @@ -7,7 +7,7 @@ import torchvision.datasets import torchvision.models import torchvision.transforms as T -from torchvision.models import efficientnet_b0, EfficientNet_B0_Weights +from torchvision.models import EfficientNet_B0_Weights, efficientnet_b0 transform = T.Compose( [T.Resize(224), T.ToTensor(), T.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))] diff --git a/examples/pytorch2/build.envd b/examples/pytorch2/build.envd index 0aeb0f570..7a4965a0e 100644 --- a/examples/pytorch2/build.envd +++ b/examples/pytorch2/build.envd @@ -2,7 +2,7 @@ envdlib = include("https://github.com/tensorchord/envdlib") def build(): - base(language="python3", image="ghcr.io/pytorch/pytorch-nightly:latest") + base(image="ghcr.io/pytorch/pytorch-nightly:latest", dev=True) shell("zsh") install.python_packages( diff --git a/examples/pytorch2/main.py b/examples/pytorch2/main.py index 98eceb755..a29ae541e 100644 --- a/examples/pytorch2/main.py +++ b/examples/pytorch2/main.py @@ -7,7 +7,7 @@ import torchvision.datasets import torchvision.models import torchvision.transforms as T -from torchvision.models import efficientnet_b0, EfficientNet_B0_Weights +from torchvision.models import EfficientNet_B0_Weights, efficientnet_b0 transform = T.Compose( [T.Resize(224), T.ToTensor(), T.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))] diff --git a/examples/r-basic/build.envd b/examples/r-basic/build.envd index b30cce31f..3f84fb662 100644 --- a/examples/r-basic/build.envd +++ b/examples/r-basic/build.envd @@ -1,5 +1,6 @@ def build(): - base(os="ubuntu20.04", language="r") + base(dev=True) + install.r_lang() install.r_packages( [ "remotes", diff --git a/examples/resnet-serving-v1/client.py b/examples/resnet-serving-v1/client.py index 685b70b0f..d8266c060 100644 --- a/examples/resnet-serving-v1/client.py +++ b/examples/resnet-serving-v1/client.py @@ -13,6 +13,8 @@ # limitations under the License. """Example: Sample Resnet client.""" +from http import HTTPStatus + import httpx import msgpack # type: ignore @@ -25,7 +27,7 @@ "http://localhost:8000/inference", data=msgpack.packb({"image": dog_bytes}), ) -if prediction.status_code == 200: +if prediction.status_code == HTTPStatus.OK: print(msgpack.unpackb(prediction.content)) else: print(prediction.status_code, prediction.content) diff --git a/examples/resnet-serving-v1/main.py b/examples/resnet-serving-v1/main.py index bce3564a0..628d50fa0 100644 --- a/examples/resnet-serving-v1/main.py +++ b/examples/resnet-serving-v1/main.py @@ -21,12 +21,11 @@ import numpy as np # type: ignore import torch # type: ignore import torchvision # type: ignore -from PIL import Image # type: ignore -from torchvision import transforms # type: ignore - from mosec import Server, Worker from mosec.errors import ValidationError from mosec.mixin import MsgpackMixin +from PIL import Image # type: ignore +from torchvision import transforms # type: ignore logger = logging.getLogger() logger.setLevel(logging.DEBUG) diff --git a/examples/stable-diffusion/README.md b/examples/stable-diffusion/README.md index 48e60e0c3..9357c4673 100644 --- a/examples/stable-diffusion/README.md +++ b/examples/stable-diffusion/README.md @@ -3,7 +3,7 @@ ## Requirements <ol> - <li><a href="https://huggingface.co/join">Sign up to Huggingface</a></li> + <li><a href="https://huggingface.co/login">Log In HuggingFace</a></li> <li><a href="https://huggingface.co/CompVis/stable-diffusion-v1-4">Accept the Stable Diffusion models agreement</a></li> <li><a href="https://huggingface.co/settings/tokens">Create an Access Token</a>. Youโ€™ll use it in the Python script below.</li> </ol> diff --git a/examples/stable-diffusion/build.envd b/examples/stable-diffusion/build.envd index 37fadf56b..dd9b7db8a 100644 --- a/examples/stable-diffusion/build.envd +++ b/examples/stable-diffusion/build.envd @@ -1,5 +1,7 @@ def build(): - base(os="ubuntu20.04", language="python") + base(dev=True) + install.conda() + install.python() # config.pip_index(url = "https://pypi.tuna.tsinghua.edu.cn/simple") install.python_packages( [ @@ -8,7 +10,7 @@ def build(): "diffusers", ] ) - # if you need run in cuda enviroment, you can add this line below, change cuda and cudnn version to your owner + # if you need run in cuda environment, you can add this line below, change cuda and cudnn version to your owner install.cuda(version="11.2.2", cudnn="8") runtime.mount( host_path="~/.cache/huggingface", envd_path="/home/envd/.cache/huggingface" diff --git a/examples/stable-diffusion/main.py b/examples/stable-diffusion/main.py index 51c56a57a..409b3ed49 100644 --- a/examples/stable-diffusion/main.py +++ b/examples/stable-diffusion/main.py @@ -1,9 +1,8 @@ +import os import random import sys -import os from diffusers import StableDiffusionPipeline -import torch from torch import autocast device = "cuda" diff --git a/examples/streamlit-hello/build.envd b/examples/streamlit-hello/build.envd index a840563d5..62b077c5f 100644 --- a/examples/streamlit-hello/build.envd +++ b/examples/streamlit-hello/build.envd @@ -1,5 +1,7 @@ def build(): - base(os="ubuntu20.04", language="python") + base(dev=True) + install.conda() + install.python() configure_streamlit(8501) diff --git a/examples/streamlit-mnist/app.py b/examples/streamlit-mnist/app.py index f705e27b9..0b6be82e9 100644 --- a/examples/streamlit-mnist/app.py +++ b/examples/streamlit-mnist/app.py @@ -1,9 +1,8 @@ -import os -import numpy as np import cv2 -from tensorflow.keras.models import load_model +import numpy as np import streamlit as st from streamlit_drawable_canvas import st_canvas +from tensorflow.keras.models import load_model model = load_model("model") diff --git a/examples/streamlit-mnist/build.envd b/examples/streamlit-mnist/build.envd index b71f33d7a..2bcdc0cb7 100644 --- a/examples/streamlit-mnist/build.envd +++ b/examples/streamlit-mnist/build.envd @@ -1,5 +1,7 @@ def build(): - base(os="ubuntu20.04", language="python3") + base(dev=True) + install.conda() + install.python() install.vscode_extensions( [ "ms-python.python", @@ -14,7 +16,8 @@ def build(): def serve(): - base(os="ubuntu20.04", language="python3") + base(dev=False) + install.python() configure_streamlit(8501) configure_mnist() diff --git a/examples/streamlit-mnist/train.ipynb b/examples/streamlit-mnist/train.ipynb index 392531e9e..d4c7b7199 100644 --- a/examples/streamlit-mnist/train.ipynb +++ b/examples/streamlit-mnist/train.ipynb @@ -6,8 +6,8 @@ "metadata": {}, "outputs": [], "source": [ - "import numpy as np\n", - "import matplotlib.pyplot as plt" + "import matplotlib.pyplot as plt\n", + "import numpy as np" ] }, { diff --git a/go.mod b/go.mod index ca73088f2..2cd13c997 100644 --- a/go.mod +++ b/go.mod @@ -1,144 +1,240 @@ module github.com/tensorchord/envd -go 1.18 +go 1.24 + +toolchain go1.24.2 require ( github.com/Pallinder/go-randomdata v1.2.0 - github.com/charmbracelet/lipgloss v0.6.0 - github.com/cockroachdb/errors v1.9.0 - github.com/containerd/console v1.0.3 - github.com/containerd/containerd v1.6.14 - github.com/creack/pty v1.1.18 - github.com/docker/docker v23.0.0-rc.1+incompatible - github.com/docker/go-connections v0.4.0 + github.com/bcicen/ctop v0.7.7 + github.com/charmbracelet/bubbletea v1.3.5 + github.com/charmbracelet/lipgloss v1.1.0 + github.com/cockroachdb/errors v1.12.0 + github.com/containerd/console v1.0.5 + github.com/containerd/errdefs v1.0.0 + github.com/containerd/log v0.1.0 + github.com/containers/image/v5 v5.35.0 + github.com/creack/pty v1.1.24 + github.com/docker/cli v28.2.2+incompatible + github.com/docker/docker v28.2.2+incompatible + github.com/docker/go-connections v0.5.0 github.com/docker/go-units v0.5.0 github.com/gizak/termui/v3 v3.1.0 - github.com/gliderlabs/ssh v0.3.5 - github.com/go-git/go-git/v5 v5.4.2 + github.com/gliderlabs/ssh v0.3.8 + github.com/go-git/go-git/v5 v5.16.0 github.com/golang/mock v1.6.0 - github.com/google/uuid v1.3.0 - github.com/moby/buildkit v0.11.0-rc3.0.20230112115050-60e82c1bcdd7 - github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 + github.com/google/uuid v1.6.0 + github.com/hashicorp/go-getter v1.7.8 + github.com/mattn/go-isatty v0.0.20 + github.com/moby/buildkit v0.22.0 + github.com/moby/term v0.5.2 github.com/morikuni/aec v1.0.0 - github.com/olekukonko/tablewriter v0.0.5 - github.com/onsi/ginkgo/v2 v2.7.0 - github.com/onsi/gomega v1.25.0 + github.com/olekukonko/tablewriter v1.0.7 + github.com/onsi/ginkgo/v2 v2.23.4 + github.com/onsi/gomega v1.36.3 github.com/opencontainers/go-digest v1.0.0 - github.com/opencontainers/image-spec v1.1.0-rc2 - github.com/pkg/sftp v1.13.5 - github.com/segmentio/analytics-go/v3 v3.2.1 - github.com/sirupsen/logrus v1.9.0 - github.com/spf13/viper v1.14.0 - github.com/stretchr/testify v1.8.1 - github.com/tensorchord/envd-server v0.0.25 - github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea - github.com/tonistiigi/vt100 v0.0.0-20210615222946-8066bb97264f - github.com/urfave/cli/v2 v2.24.1 - go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd - golang.org/x/crypto v0.5.0 - golang.org/x/sync v0.1.0 - golang.org/x/term v0.4.0 - golang.org/x/time v0.1.0 -) - -require github.com/moby/patternmatcher v0.5.0 // indirect - -require ( - github.com/aymanbagabas/go-osc52 v1.0.3 // indirect - github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect - github.com/charmbracelet/bubbletea v0.23.1 - github.com/containerd/ttrpc v1.1.0 // indirect - github.com/klauspost/compress v1.15.12 // indirect - github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/mattn/go-isatty v0.0.17 - github.com/mattn/go-localereader v0.0.1 // indirect - github.com/moby/locker v1.0.1 // indirect - github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect - github.com/muesli/cancelreader v0.2.2 // indirect - github.com/muesli/reflow v0.3.0 // indirect - github.com/muesli/termenv v0.13.0 // indirect - github.com/op/go-logging v0.0.0-20160211212156-b2cb9fa56473 // indirect + github.com/opencontainers/image-spec v1.1.1 github.com/pkg/errors v0.9.1 - github.com/rivo/uniseg v0.2.0 // indirect - github.com/segmentio/backo-go v1.0.0 // indirect + github.com/pkg/sftp v1.13.9 + github.com/schollz/progressbar/v3 v3.18.0 + github.com/segmentio/analytics-go/v3 v3.3.0 + github.com/sirupsen/logrus v1.9.3 + github.com/spf13/viper v1.20.1 + github.com/stretchr/testify v1.10.0 + github.com/syncthing/syncthing v1.29.6 + github.com/tensorchord/envd-server v0.0.27 + github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea + github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab + github.com/urfave/cli/v2 v2.27.6 + go.starlark.net v0.0.0-20250530210732-c81913c6f2e2 + golang.org/x/crypto v0.38.0 + golang.org/x/sync v0.14.0 + golang.org/x/term v0.32.0 + golang.org/x/time v0.11.0 ) require ( - github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/Microsoft/go-winio v0.5.2 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect - github.com/acomagu/bufpipe v1.0.3 // indirect + cel.dev/expr v0.24.0 // indirect + cloud.google.com/go v0.121.2 // indirect + cloud.google.com/go/auth v0.16.1 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect + cloud.google.com/go/compute/metadata v0.7.0 // indirect + cloud.google.com/go/iam v1.5.2 // indirect + cloud.google.com/go/monitoring v1.24.2 // indirect + cloud.google.com/go/storage v1.55.0 // indirect + dario.cat/mergo v1.0.2 // indirect + github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect + github.com/BurntSushi/toml v1.5.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.28.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.52.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.52.0 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/ProtonMail/go-crypto v1.3.0 // indirect github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect - github.com/bcicen/ctop v0.7.7 - github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f // indirect - github.com/cockroachdb/redact v1.1.3 // indirect - github.com/containerd/continuity v0.3.0 // indirect - github.com/containerd/typeurl v1.0.2 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/cli v23.0.0-rc.1+incompatible - github.com/docker/distribution v2.8.1+incompatible // indirect - github.com/docker/docker-credential-helpers v0.7.0 // indirect - github.com/emirpasic/gods v1.12.0 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/getsentry/sentry-go v0.12.0 // indirect - github.com/go-git/gcfg v1.5.0 // indirect - github.com/go-git/go-billy/v5 v5.3.1 // indirect - github.com/go-logr/logr v1.2.3 // indirect + github.com/aws/aws-sdk-go v1.55.7 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/charmbracelet/colorprofile v0.3.1 // indirect + github.com/charmbracelet/x/ansi v0.9.2 // indirect + github.com/charmbracelet/x/cellbuf v0.0.13 // indirect + github.com/charmbracelet/x/term v0.2.1 // indirect + github.com/cloudflare/circl v1.6.1 // indirect + github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect + github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 // indirect + github.com/cockroachdb/redact v1.1.6 // indirect + github.com/containerd/containerd/api v1.9.0 // indirect + github.com/containerd/containerd/v2 v2.1.1 // indirect + github.com/containerd/continuity v0.4.5 // indirect + github.com/containerd/errdefs/pkg v0.3.0 // indirect + github.com/containerd/platforms v1.0.0-rc.1 // indirect + github.com/containerd/ttrpc v1.2.7 // indirect + github.com/containerd/typeurl/v2 v2.2.3 // indirect + github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect + github.com/containers/ocicrypt v1.2.1 // indirect + github.com/containers/storage v1.58.0 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect + github.com/cyphar/filepath-securejoin v0.4.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/docker/distribution v2.8.3+incompatible // indirect + github.com/docker/docker-credential-helpers v0.9.3 // indirect + github.com/ebitengine/purego v0.8.4 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect + github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect + github.com/fatih/color v1.18.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/getsentry/sentry-go v0.33.0 // indirect + github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect + github.com/go-git/go-billy/v5 v5.6.2 // indirect + github.com/go-jose/go-jose/v4 v4.1.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/gofrs/flock v0.8.1 // indirect - github.com/gogo/googleapis v1.4.1 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/go-viper/mapstructure/v2 v2.3.0 // indirect + github.com/gofrs/flock v0.12.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/golang/snappy v1.0.0 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect + github.com/google/s2a-go v0.1.9 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect - github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect - github.com/imdario/mergo v0.3.13 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect + github.com/googleapis/gax-go/v2 v2.14.2 // indirect + github.com/gorilla/mux v1.8.1 // indirect + github.com/greatroar/blobloom v0.8.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-safetemp v1.0.0 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/in-toto/in-toto-golang v0.9.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect - github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/klauspost/compress v1.18.0 // indirect github.com/kr/fs v0.1.0 // indirect - github.com/kr/pretty v0.3.0 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect - github.com/magiconair/properties v1.8.6 // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75 // indirect + github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/moby/sys/signal v0.7.0 // indirect - github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d // indirect - github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.6 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rogpeppe/go-internal v1.8.1 // indirect + github.com/mitchellh/go-testing-interface v1.14.1 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/locker v1.0.1 // indirect + github.com/moby/patternmatcher v0.6.0 // indirect + github.com/moby/sys/capability v0.4.0 // indirect + github.com/moby/sys/mountinfo v0.7.2 // indirect + github.com/moby/sys/signal v0.7.1 // indirect + github.com/moby/sys/user v0.4.0 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/muesli/termenv v0.16.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/nsf/termbox-go v1.1.1 // indirect + github.com/olekukonko/errors v1.1.0 // indirect + github.com/olekukonko/ll v0.0.8 // indirect + github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 // indirect + github.com/opencontainers/runtime-spec v1.2.1 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/pierrec/lz4/v4 v4.1.22 // indirect + github.com/pjbgf/sha1cd v0.3.2 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.64.0 // indirect + github.com/prometheus/procfs v0.16.1 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/sergi/go-diff v1.1.0 // indirect - github.com/spf13/afero v1.9.2 // indirect - github.com/spf13/cast v1.5.0 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/subosito/gotenv v1.4.1 // indirect - github.com/tonistiigi/fsutil v0.0.0-20230105215944-fb433841cbfa // indirect - github.com/xanzy/ssh-agent v0.3.0 // indirect - github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.29.0 // indirect - go.opentelemetry.io/otel v1.4.1 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.1 // indirect - go.opentelemetry.io/otel/sdk v1.4.1 // indirect - go.opentelemetry.io/otel/trace v1.4.1 // indirect - go.opentelemetry.io/proto/otlp v0.12.0 // indirect - golang.org/x/net v0.5.0 // indirect - golang.org/x/sys v0.4.0 // indirect - golang.org/x/text v0.6.0 // indirect - google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e // indirect - google.golang.org/grpc v1.50.1 // indirect - google.golang.org/protobuf v1.28.1 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect + github.com/sagikazarmark/locafero v0.9.0 // indirect + github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect + github.com/segmentio/backo-go v1.1.0 // indirect + github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect + github.com/shibumi/go-pathspec v1.3.0 // indirect + github.com/shirou/gopsutil/v4 v4.25.5 // indirect + github.com/skeema/knownhosts v1.3.1 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.14.0 // indirect + github.com/spf13/cast v1.8.0 // indirect + github.com/spf13/pflag v1.0.6 // indirect + github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/syncthing/notify v0.0.0-20250528144937-c7027d4f7465 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect + github.com/thejerf/suture/v4 v4.0.6 // indirect + github.com/tklauser/go-sysconf v0.3.15 // indirect + github.com/tklauser/numcpus v0.10.0 // indirect + github.com/tonistiigi/fsutil v0.0.0-20250417144416-3f76f8130144 // indirect + github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 // indirect + github.com/ulikunitz/xz v0.5.12 // indirect + github.com/xanzy/ssh-agent v0.3.3 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect + github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + github.com/zeebo/errs v1.4.0 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.36.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect + go.opentelemetry.io/otel v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect + go.opentelemetry.io/otel/metric v1.36.0 // indirect + go.opentelemetry.io/otel/sdk v1.36.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect + go.opentelemetry.io/otel/trace v1.36.0 // indirect + go.opentelemetry.io/proto/otlp v1.7.0 // indirect + go.uber.org/automaxprocs v1.6.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/net v0.40.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/text v0.25.0 // indirect + golang.org/x/tools v0.32.0 // indirect + google.golang.org/api v0.235.0 // indirect + google.golang.org/genproto v0.0.0-20250528174236-200df99c418a // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect + google.golang.org/grpc v1.72.2 // indirect + google.golang.org/protobuf v1.36.6 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) - -// Copied from buildkit to make github.com/tonistiigi/fsutil happy. -replace github.com/docker/docker => github.com/docker/docker v20.10.3-0.20220224222438-c78f6963a1c0+incompatible diff --git a/go.sum b/go.sum index 0e48cc67a..fe5f8e49a 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,6 @@ bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= +cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= +cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -18,247 +20,925 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.104.0 h1:gSmWO7DY1vOm0MVU6DNXM11BWHHsTUmsC5cv1fuW5X8= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= +cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= +cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= +cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= +cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= +cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= +cloud.google.com/go v0.121.2 h1:v2qQpN6Dx9x2NmwrqlesOt3Ys4ol5/lFZ6Mg1B7OJCg= +cloud.google.com/go v0.121.2/go.mod h1:nRFlrHq39MNVWu+zESP2PosMWA0ryJw8KUBZ2iZpxbw= +cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= +cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= +cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= +cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= +cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= +cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= +cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= +cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= +cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= +cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= +cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= +cloud.google.com/go/aiplatform v1.36.1/go.mod h1:WTm12vJRPARNvJ+v6P52RDHCNe4AhvjcIZ/9/RRHy/k= +cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= +cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= +cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= +cloud.google.com/go/analytics v0.17.0/go.mod h1:WXFa3WSym4IZ+JiKmavYdJwGG/CvpqiqczmL59bTD9M= +cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= +cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= +cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= +cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= +cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= +cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= +cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= +cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= +cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= +cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= +cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= +cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= +cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= +cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= +cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= +cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= +cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= +cloud.google.com/go/appengine v1.7.0/go.mod h1:eZqpbHFCqRGa2aCdope7eC0SWLV1j0neb/QnMJVWx6A= +cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= +cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= +cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= +cloud.google.com/go/area120 v0.7.0/go.mod h1:a3+8EUD1SX5RUcCs3MY5YasiO1z6yLiNLRiFrykbynY= +cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= +cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= +cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= +cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= +cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= +cloud.google.com/go/artifactregistry v1.11.1/go.mod h1:lLYghw+Itq9SONbCa1YWBoWs1nOucMH0pwXN1rOBZFI= +cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= +cloud.google.com/go/artifactregistry v1.12.0/go.mod h1:o6P3MIvtzTOnmvGagO9v/rOjjA0HmhJ+/6KAXrmYDCI= +cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= +cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= +cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= +cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= +cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= +cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= +cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= +cloud.google.com/go/asset v1.12.0/go.mod h1:h9/sFOa4eDIyKmH6QMpm4eUK3pDojWnUhTgJlk762Hg= +cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= +cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= +cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= +cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= +cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= +cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= +cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= +cloud.google.com/go/auth v0.16.1 h1:XrXauHMd30LhQYVRHLGvJiYeczweKQXZxsTbV9TiguU= +cloud.google.com/go/auth v0.16.1/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= +cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= +cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= +cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= +cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= +cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= +cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= +cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= +cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= +cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= +cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= +cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= +cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= +cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= +cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= +cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.12.1 h1:gKVJMEyqV5c/UnpzjjQbo3Rjvvqpr9B1DFSbJC4OXr0= -cloud.google.com/go/compute/metadata v0.2.1 h1:efOwf5ymceDhK6PKMnnrTHP4pppY5L22mle96M1yP48= +cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= +cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= +cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= +cloud.google.com/go/bigquery v1.47.0/go.mod h1:sA9XOgy0A8vQK9+MWhEQTY6Tix87M/ZurWFIxmF9I/E= +cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= +cloud.google.com/go/bigquery v1.49.0/go.mod h1:Sv8hMmTFFYBlt/ftw2uN6dFdQPzBlREY9yBh7Oy7/4Q= +cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= +cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= +cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= +cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= +cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= +cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= +cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= +cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= +cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= +cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= +cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= +cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= +cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= +cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= +cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= +cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= +cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= +cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= +cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= +cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= +cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= +cloud.google.com/go/cloudbuild v1.6.0/go.mod h1:UIbc/w9QCbH12xX+ezUsgblrWv+Cv4Tw83GiSMHOn9M= +cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= +cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= +cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= +cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= +cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= +cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= +cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= +cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= +cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= +cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= +cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= +cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= +cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= +cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= +cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= +cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= +cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= +cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= +cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= +cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= +cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= +cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU= +cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo= +cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= +cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= +cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= +cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= +cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= +cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= +cloud.google.com/go/container v1.14.0/go.mod h1:3AoJMPhHfLDxLvrlVWaK57IXzaPnLaZq63WX59aQBfM= +cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= +cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= +cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= +cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= +cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= +cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= +cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= +cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= +cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= +cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= +cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= +cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= +cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= +cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= +cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= +cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= +cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= +cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= +cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= +cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= +cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= +cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= +cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= +cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= +cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= +cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= +cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= +cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= +cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= +cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= +cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= +cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= +cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= +cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= +cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= +cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= +cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= +cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= +cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= +cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= +cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= +cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= +cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= +cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= +cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= +cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= +cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= +cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= +cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= +cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= +cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= +cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= +cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= +cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= +cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= +cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= +cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= +cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= +cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= +cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= +cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= +cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= +cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= +cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= +cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= +cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= +cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= +cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= +cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= +cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= +cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= +cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= +cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= +cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= +cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= +cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= +cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= +cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= +cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= +cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= +cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= +cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= +cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= +cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= +cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= +cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= +cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= +cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= +cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= +cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= +cloud.google.com/go/functions v1.12.0/go.mod h1:AXWGrF3e2C/5ehvwYo/GH6O5s09tOPksiKhz+hH8WkA= +cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= +cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= +cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= +cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= +cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= +cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= +cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= +cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= +cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= +cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= +cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= +cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= +cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= +cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= +cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= +cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= +cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= +cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= +cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= +cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= +cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= +cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= +cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= +cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= +cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= +cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= +cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= +cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= +cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= +cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= +cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= +cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8= +cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE= +cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= +cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= +cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= +cloud.google.com/go/iap v1.7.0/go.mod h1:beqQx56T9O1G1yNPph+spKpNibDlYIiIixiqsQXxLIo= +cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= +cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= +cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= +cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= +cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= +cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= +cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= +cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= +cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= +cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= +cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= +cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg= +cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= +cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24= +cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= +cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= +cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= +cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= +cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= +cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= +cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= +cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= +cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= +cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= +cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= +cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc= +cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA= +cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= +cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= +cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= +cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE= +cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY= +cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= +cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= +cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= +cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= +cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= +cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= +cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= +cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= +cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= +cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= +cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= +cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= +cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= +cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= +cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= +cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= +cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= +cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= +cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= +cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= +cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= +cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= +cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= +cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM= +cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U= +cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= +cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= +cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= +cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= +cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= +cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= +cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= +cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= +cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= +cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= +cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= +cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= +cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= +cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= +cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= +cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= +cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= +cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= +cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= +cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= +cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= +cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= +cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= +cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= +cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= +cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= +cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= +cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= +cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= +cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= +cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= +cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= +cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= +cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= +cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= +cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= +cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= +cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= +cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= +cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= +cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= +cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= +cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= +cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= +cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= +cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= +cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= +cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= +cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= +cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= +cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= +cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= +cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= +cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= +cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= +cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= +cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= +cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= +cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= +cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= +cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= +cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= +cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= +cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= +cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= +cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= +cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= +cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= +cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= +cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= +cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= +cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= +cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= +cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= +cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= +cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= +cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= +cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= +cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= +cloud.google.com/go/resourcemanager v1.6.0/go.mod h1:YcpXGRs8fDzcUl1Xw8uOVmI8JEadvhRIkoXXUNVYcVo= +cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= +cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= +cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= +cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= +cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= +cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= +cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= +cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= +cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= +cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= +cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= +cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= +cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= +cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= +cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= +cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= +cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= +cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= +cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= +cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= +cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= +cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= +cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= +cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= +cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= +cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= +cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= +cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= +cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= +cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= +cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= +cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= +cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= +cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= +cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= +cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= +cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= +cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= +cloud.google.com/go/servicecontrol v1.10.0/go.mod h1:pQvyvSRh7YzUF2efw7H87V92mxU8FnFDawMClGCNuAA= +cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= +cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= +cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= +cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= +cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= +cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= +cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= +cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= +cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= +cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= +cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= +cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= +cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= +cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= +cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= +cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= +cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= +cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= +cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= +cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= +cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= +cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= +cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= +cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= +cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= +cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= +cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= +cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= +cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= +cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= +cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= +cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= +cloud.google.com/go/storage v1.55.0 h1:NESjdAToN9u1tmhVqhXCaCwYBuvEhZLLv0gBr+2znf0= +cloud.google.com/go/storage v1.55.0/go.mod h1:ztSmTTwzsdXe5syLVS0YsbFxXuvEmEyZj7v7zChEmuY= +cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= +cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= +cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= +cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= +cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= +cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= +cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= +cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= +cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= +cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= +cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= +cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= +cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= +cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= +cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= +cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= +cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= +cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= +cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= +cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4= +cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI= +cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= +cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= +cloud.google.com/go/translate v1.5.0/go.mod h1:29YDSYveqqpA1CQFD7NQuP49xymq17RXNaUDdc0mNu0= +cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= +cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= +cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= +cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= +cloud.google.com/go/video v1.12.0/go.mod h1:MLQew95eTuaNDEGriQdcYn0dTwf9oWiA4uYebxM5kdg= +cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= +cloud.google.com/go/video v1.14.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= +cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= +cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= +cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= +cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= +cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= +cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= +cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= +cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= +cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= +cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= +cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= +cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= +cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= +cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= +cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= +cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= +cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= +cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= +cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= +cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= +cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= +cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= +cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= +cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= +cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= +cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= +cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= +cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= +cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= +cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= +cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= +cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= +cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= +cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= +cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= +cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= +dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= +dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= +gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= +git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= +github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw= -github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= -github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w= -github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= -github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= -github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.28.0 h1:VaFXBL0NJpiFBtw4aVJpKHeKULVTcHpD+/G0ibZkcBw= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.28.0/go.mod h1:JXkPazkEc/dZTHzOlzv2vT1DlpWSTbSLmu/1KY6Ly0I= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.52.0 h1:QFgWzcdmJlgEAwJz/zePYVJQxfoJGRtgIqZfIUFg5oQ= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.52.0/go.mod h1:ayYHuYU7iNcNtEs1K9k6D/Bju7u1VEHMQm5qQ1n3GtM= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.52.0 h1:0l8ynskVvq1dvIn5vJbFMf/a/3TqFpRmCMrruFbzlvk= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.52.0/go.mod h1:f/ad5NuHnYz8AOZGuR0cY+l36oSCstdxD73YlIchr6I= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.52.0 h1:wbMd4eG/fOhsCa6+IP8uEDvWF5vl7rNoUWmP5f72Tbs= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.52.0/go.mod h1:gdIm9TxRk5soClCwuB0FtdXsbqtw0aqPwBEurK9tPkw= +github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/Microsoft/go-winio v0.4.15-0.20200908182639-5b44b70ab3ab/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Microsoft/hcsshim v0.8.10/go.mod h1:g5uw8EV2mAlzqe94tfNBNdr89fnbD/n3HV0OhsddkmM= -github.com/Microsoft/hcsshim v0.9.6 h1:VwnDOgLeoi2du6dAznfmspNqTiwczvjv4K7NxuY9jsY= +github.com/Microsoft/hcsshim v0.13.0 h1:/BcXOiS6Qi7N9XqUcv27vkIuVOkBEcWstd2pMlWSeaA= +github.com/Microsoft/hcsshim v0.13.0/go.mod h1:9KWJ/8DgU+QzYGupX4tzMhRQE8h6w90lH6HAaclpEok= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Pallinder/go-randomdata v1.2.0 h1:DZ41wBchNRb/0GfsePLiSwb0PHZmT67XY00lCDlaYPg= github.com/Pallinder/go-randomdata v1.2.0/go.mod h1:yHmJgulpD2Nfrm0cR9tI/+oAgRqCQQixsA8HyRZfV9Y= -github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ= -github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= -github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= -github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= -github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= -github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= +github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= +github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= +github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= +github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc= +github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= +github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= +github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/aymanbagabas/go-osc52 v1.0.3 h1:DTwqENW7X9arYimJrPeGZcV0ln14sGMt3pHZspWD+Mg= -github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= -github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= +github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE= +github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/bcicen/ctop v0.7.7 h1:huwTYTAHqqObovkg50v8qdTCLSDvPCBvS+k8EM0tP7E= github.com/bcicen/ctop v0.7.7/go.mod h1:jO0mmP/wGx4ZEGCWAoMx8DYFNTYNs6YG8wzCGj+DX+Q= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/c9s/goprocinfo v0.0.0-20170609001544-b34328d6e0cd/go.mod h1:uEyr4WpAH4hio6LFriaPkL938XnrvLpNPmQHBdrmbIE= -github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/charmbracelet/bubbletea v0.23.1 h1:CYdteX1wCiCzKNUlwm25ZHBIc1GXlYFyUIte8WPvhck= -github.com/charmbracelet/bubbletea v0.23.1/go.mod h1:JAfGK/3/pPKHTnAS8JIE2u9f61BjWTQY57RbT25aMXU= -github.com/charmbracelet/lipgloss v0.6.0 h1:1StyZB9vBSOyuZxQUcUwGr17JmojPNm87inij9N3wJY= -github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/charmbracelet/bubbletea v1.3.5 h1:JAMNLTbqMOhSwoELIr0qyP4VidFq72/6E9j7HHmRKQc= +github.com/charmbracelet/bubbletea v1.3.5/go.mod h1:TkCnmH+aBd4LrXhXcqrKiYwRs7qyQx5rBgH5fVY3v54= +github.com/charmbracelet/colorprofile v0.3.1 h1:k8dTHMd7fgw4bnFd7jXTLZrSU/CQrKnL3m+AxCzDz40= +github.com/charmbracelet/colorprofile v0.3.1/go.mod h1:/GkGusxNs8VB/RSOh3fu0TJmQ4ICMMPApIIVn0KszZ0= +github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= +github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= +github.com/charmbracelet/x/ansi v0.9.2 h1:92AGsQmNTRMzuzHEYfCdjQeUzTrgE1vfO5/7fEVoXdY= +github.com/charmbracelet/x/ansi v0.9.2/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE= +github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k= +github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= +github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= +github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= +github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= +github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM= +github.com/chengxilo/virtualterm v1.0.4/go.mod h1:DyxxBZz/x1iqJjFxTFcr6/x+jSpqN0iwWCOK1q10rlY= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= +github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/datadriven v1.0.1-0.20211007161720-b558070c3be0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4= -github.com/cockroachdb/datadriven v1.0.1-0.20220214170620-9913f5bc19b7/go.mod h1:hi0MtSY3AYDQNDi83kDkMH5/yqM/CsIrsOITkSoH7KI= -github.com/cockroachdb/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM= -github.com/cockroachdb/errors v1.8.8/go.mod h1:z6VnEL3hZ/2ONZEvG7S5Ym0bU2AqPcEKnIiA1wbsSu0= -github.com/cockroachdb/errors v1.9.0 h1:B48dYem5SlAY7iU8AKsgedb4gH6mo+bDkbtLIvM/a88= -github.com/cockroachdb/errors v1.9.0/go.mod h1:vaNcEYYqbIqB5JhKBhFV9CneUqeuEbB2OYJBK4GBNYQ= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f h1:6jduT9Hfc0njg5jJ1DdKCFPdMBrp/mdZfCpa5h+WM74= -github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= -github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= -github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= -github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= -github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= +github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls= +github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cockroachdb/errors v1.12.0 h1:d7oCs6vuIMUQRVbi6jWWWEJZahLCfJpnJSVobd1/sUo= +github.com/cockroachdb/errors v1.12.0/go.mod h1:SvzfYNNBshAVbZ8wzNc/UPK3w1vf0dKDUP41ucAIf7g= +github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 h1:ASDL+UJcILMqgNeV5jiqR4j+sTuvQNHdf2chuKj1M5k= +github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506/go.mod h1:Mw7HqKr2kdtu6aYGn3tPmAftiP3QPX63LdK/zcariIo= +github.com/cockroachdb/redact v1.1.6 h1:zXJBwDZ84xJNlHl1rMyCojqyIxv+7YUpQiJLQ7n4314= +github.com/cockroachdb/redact v1.1.6/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= +github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= +github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59 h1:qWj4qVYZ95vLWwqyNJCQg7rDsG5wPdze0UaPolH7DUk= github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= -github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA= +github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo= +github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= -github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= +github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc= +github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.6.14 h1:W+d0AJKVG3ioTZZyQwcw1Y3vvo6ZDYzAcjDcY4tkgGI= -github.com/containerd/containerd v1.6.14/go.mod h1:U2NnBPIhzJDm59xF7xB2MMHnKtggpZ+phKg8o2TKj2c= +github.com/containerd/containerd/api v1.9.0 h1:HZ/licowTRazus+wt9fM6r/9BQO7S0vD5lMcWspGIg0= +github.com/containerd/containerd/api v1.9.0/go.mod h1:GhghKFmTR3hNtyznBoQ0EMWr9ju5AqHjcZPsSpTKutI= +github.com/containerd/containerd/v2 v2.1.1 h1:znnkm7Ajz8lg8BcIPMhc/9yjBRN3B+OkNKqKisKfwwM= +github.com/containerd/containerd/v2 v2.1.1/go.mod h1:zIfkQj4RIodclYQkX7GSSswSwgP8d/XxDOtOAoSDIGU= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20200928162600-f2cc35102c2a/go.mod h1:W0qIOTD7mp2He++YVq+kgfXezRYqzP1uDuMVH1bITDY= -github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= -github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= +github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4= +github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v1.0.0 h1:6PirWBr9/L7GDamKr+XM0IeUFXu5mf3M/BPpH9gaLBU= +github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY= +github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o= github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/nydus-snapshotter v0.3.1 h1:b8WahTrPkt3XsabjG2o/leN4fw3HWZYr+qxo/Z8Mfzk= -github.com/containerd/stargz-snapshotter v0.13.0 h1:3zr1/IkW1aEo6cMYTQeZ4L2jSuCN+F4kgGfjnuowe4U= -github.com/containerd/stargz-snapshotter/estargz v0.13.0 h1:fD7AwuVV+B40p0d9qVkH/Au1qhp8hn/HWJHIYjpEcfw= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/nydus-snapshotter v0.15.0 h1:RqZRs1GPeM6T3wmuxJV9u+2Rg4YETVMwTmiDeX+iWC8= +github.com/containerd/nydus-snapshotter v0.15.0/go.mod h1:biq0ijpeZe0I5yZFSJyHzFSjjRZQ7P7y/OuHyd7hYOw= +github.com/containerd/platforms v1.0.0-rc.1 h1:83KIq4yy1erSRgOVHNk1HYdPvzdJ5CnsWaRoJX4C41E= +github.com/containerd/platforms v1.0.0-rc.1/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4= +github.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y= +github.com/containerd/plugin v1.0.0/go.mod h1:hQfJe5nmWfImiqT1q8Si3jLv3ynMUIBB47bQ+KexvO8= +github.com/containerd/stargz-snapshotter v0.16.3 h1:zbQMm8dRuPHEOD4OqAYGajJJUwCeUzt4j7w9Iaw58u4= +github.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRccTampEyKpjpOnS3CyiV1Ebr8= +github.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/ttrpc v1.1.0 h1:GbtyLRxb0gOLR0TYQWt3O6B0NvT8tMdorEHqIQo/lWI= -github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ= +github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ= +github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= -github.com/containerd/typeurl v1.0.2 h1:Chlt8zIieDbzQFzXzAeBEF92KhExuE4p9p92/QmY7aY= -github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40= +github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk= +github.com/containers/image/v5 v5.35.0 h1:T1OeyWp3GjObt47bchwD9cqiaAm/u4O4R9hIWdrdrP8= +github.com/containers/image/v5 v5.35.0/go.mod h1:8vTsgb+1gKcBL7cnjyNOInhJQfTUQjJoO2WWkKDoebM= +github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA= +github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= +github.com/containers/ocicrypt v1.2.1 h1:0qIOTT9DoYwcKmxSt8QJt+VzMY18onl9jUXsxpVhSmM= +github.com/containers/ocicrypt v1.2.1/go.mod h1:aD0AAqfMp0MtwqWgHM1bUwe1anx0VazI108CRrSKINQ= +github.com/containers/storage v1.58.0 h1:Q7SyyCCjqgT3wYNgRNIL8o/wUS92heIj2/cc8Sewvcc= +github.com/containers/storage v1.58.0/go.mod h1:w7Jl6oG+OpeLGLzlLyOZPkmUso40kjpzgrHUk5tyBlo= github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= +github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= -github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= +github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= +github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= +github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U= +github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/docker/cli v23.0.0-rc.1+incompatible h1:Vl3pcUK4/LFAD56Ys3BrqgAtuwpWd/IO3amuSL0ZbP0= -github.com/docker/cli v23.0.0-rc.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= -github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v20.10.3-0.20220224222438-c78f6963a1c0+incompatible h1:Ptj2To+ezU/mCBUKdYXBQ2r3/2EJojAlOZrsgprF+is= -github.com/docker/docker v20.10.3-0.20220224222438-c78f6963a1c0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= -github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/cli v28.2.2+incompatible h1:qzx5BNUDFqlvyq4AHzdNB7gSyVTmU4cgsyN9SdInc1A= +github.com/docker/cli v28.2.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= +github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v20.10.0-beta1.0.20201113105859-b6bfff2a628f+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v28.2.2+incompatible h1:CjwRSksz8Yo4+RmQ339Dp/D2tGO5JxwYeqtMOEe0LDw= +github.com/docker/docker v28.2.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8= +github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= -github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= -github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= +github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= +github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= +github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= +github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M= +github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA= +github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A= +github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= -github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= +github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= +github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= +github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= +github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fsouza/go-dockerclient v1.7.0/go.mod h1:Ny0LfP7OOsYu9nAi4339E4Ifor6nGBFO2M8lnd2nR+c= -github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= -github.com/getsentry/sentry-go v0.12.0 h1:era7g0re5iY13bHSdN/xMkyV+5zZppjRVQhZrXCaEIk= -github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= +github.com/getsentry/sentry-go v0.33.0 h1:YWyDii0KGVov3xOaamOnF0mjOrqSjBqwv48UEzn7QFg= +github.com/getsentry/sentry-go v0.33.0/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= -github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/gizak/termui v2.3.0+incompatible/go.mod h1:PkJoWUt/zacQKysNfQtcw1RW+eK2SxkieVBtl+4ovLA= github.com/gizak/termui/v3 v3.1.0 h1:ZZmVDgwHl7gR7elfKf1xc4IudXZ5qqfDh4wExk4Iajc= github.com/gizak/termui/v3 v3.1.0/go.mod h1:bXQEBkJpzxUAKf0+xq9MSWAvWZlE7c+aidmyFlkYTrY= -github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= -github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= -github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= -github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= -github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= -github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= -github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8= -github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= -github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4= -github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= +github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= +github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= +github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= +github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= +github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= +github.com/go-git/go-git/v5 v5.16.0 h1:k3kuOEpkc0DeY7xlL6NaaNg39xdgQbtH5mwCafHO9AQ= +github.com/go-git/go-git/v5 v5.16.0/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-jose/go-jose/v4 v4.1.0 h1:cYSYxd3pw5zd2FSXk2vGdn9igQU2PS8MuxrCOCl0FdY= +github.com/go-jose/go-jose/v4 v4.1.0/go.mod h1:GG/vqmYm3Von2nYiB2vGTXzdoNKE5tix5tuc6iAd+sw= +github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= +github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= -github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk= +github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= -github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= -github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= -github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= -github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= +github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= -github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -266,6 +946,7 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -283,11 +964,18 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= +github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -296,16 +984,24 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= +github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -316,286 +1012,355 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= +github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= +github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= +github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= +github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= +github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/googleapis/gax-go/v2 v2.14.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0= +github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w= +github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= -github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/greatroar/blobloom v0.8.0 h1:I9RlEkfqK9/6f1v9mFmDYegDQ/x0mISCpiNpAm23Pt4= +github.com/greatroar/blobloom v0.8.0/go.mod h1:mjMJ1hh1wjGVfr93QIHJ6FfDNVrA0IELv8OvMHJxHKs= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-getter v1.7.8 h1:mshVHx1Fto0/MydBekWan5zUipGq7jO0novchgMmSiY= +github.com/hashicorp/go-getter v1.7.8/go.mod h1:2c6CboOEb9jG6YvmC9xdD+tyAFsrUaJPedwXDGr0TM4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= +github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= -github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= -github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= -github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= -github.com/in-toto/in-toto-golang v0.5.0 h1:hb8bgwr0M2hGdDsLjkJ3ZqJ8JFLL/tgYdAxF/XEFBbY= +github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU= +github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= -github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= -github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI= -github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= -github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g= -github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jgautheron/codename-generator v0.0.0-20150829203204-16d037c7cc3c/go.mod h1:FJRkXmPrkHw0WDjB/LXMUhjWJ112Y6JUYnIVBOy8oH8= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= -github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= -github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= -github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= -github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk= -github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8= -github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U= -github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE= -github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw= -github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE= -github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0= -github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro= -github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8= -github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck= -github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM= -github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= -github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= +github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= -github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y= -github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= -github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= -github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr325bN2FD2ISlRRztXibcX6e8f5FR5Dc= +github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= +github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.0-20170201023540-14207d285c6c/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= -github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg= -github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ= -github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= -github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= +github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75 h1:cUVxyR+UfmdEAZGJ8IiKld1O0dbGotEnkMolG5hfMSY= +github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75/go.mod h1:pBbZyGwC5i16IBkjVKoy/sznA8jPD/K9iedwe1ESE6w= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/moby/buildkit v0.11.0-rc3.0.20230112115050-60e82c1bcdd7 h1:95HXIYYoJYHg5Il51AwxN/B3I4yu9sXPTrP0eYBbgC8= -github.com/moby/buildkit v0.11.0-rc3.0.20230112115050-60e82c1bcdd7/go.mod h1:v43oa6H2Fx/cdzc7j0UlUu8p6188yy1P3vrujAs99uw= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= +github.com/moby/buildkit v0.22.0 h1:aWN06w1YGSVN1XfeZbj2ZbgY+zi5xDAjEFI8Cy9fTjA= +github.com/moby/buildkit v0.22.0/go.mod h1:j4pP5hxiTWcz7xuTK2cyxQislHl/N2WWHzOy43DlLJw= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo= -github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= +github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= +github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk= +github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I= github.com/moby/sys/mount v0.2.0/go.mod h1:aAivFE2LB3W4bACsUXChRHQ0qKWsetY4Y9V7sxOougM= github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= -github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI= -github.com/moby/sys/signal v0.7.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= +github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= +github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= +github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0= +github.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8= +github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= +github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/term v0.0.0-20201110203204-bea5bbe245bf/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= +github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= -github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34= -github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= -github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ= -github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= -github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= -github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= -github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0= -github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= -github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= -github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM= -github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= -github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= +github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/nsf/termbox-go v0.0.0-20180303152453-e2050e41c884/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= -github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d h1:x3S6kxmy49zXVVyhcnrFqxvNVCBPb2KZ9hV2RBdS840= github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= +github.com/nsf/termbox-go v1.1.1 h1:nksUPLCb73Q++DwbYUBEglYBRPZyoXJdrj5L+TkjyZY= +github.com/nsf/termbox-go v1.1.1/go.mod h1:T0cTdVuOwf7pHQNtfhnEbzHbcNyCEcVU4YPpouCbVxo= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= +github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= +github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM= +github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y= +github.com/olekukonko/ll v0.0.8 h1:sbGZ1Fx4QxJXEqL/6IG8GEFnYojUSQ45dJVwN2FH2fc= +github.com/olekukonko/ll v0.0.8/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g= +github.com/olekukonko/tablewriter v1.0.7 h1:HCC2e3MM+2g72M81ZcJU11uciw6z/p82aEnm4/ySDGw= +github.com/olekukonko/tablewriter v1.0.7/go.mod h1:H428M+HzoUXC6JU2Abj9IT9ooRmdq9CxuDmKMtrOCMs= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= -github.com/onsi/ginkgo/v2 v2.7.0 h1:/XxtEV3I3Eif/HobnVx9YmJgk8ENdRsuUmM+fLCFNow= -github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= +github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.25.0 h1:Vw7br2PCDYijJHSfBOWhov+8cAnUf8MfMaIOV323l6Y= -github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= -github.com/op/go-logging v0.0.0-20160211212156-b2cb9fa56473 h1:J1QZwDXgZ4dJD2s19iqR9+U00OWM2kDzbf1O/fmvCWg= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU= +github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= github.com/op/go-logging v0.0.0-20160211212156-b2cb9fa56473/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= -github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.3/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= -github.com/opencontainers/runc v1.1.4 h1:nRCz/8sKg6K6jgYAFLDlXzPeITBZJyX28DBVhWD+5dg= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww= +github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= -github.com/opencontainers/selinux v1.10.2 h1:NFy2xCsjn7+WspbfZkUd5zyVeisV7VFbPSP96+8/ha4= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/package-url/packageurl-go v0.1.1-0.20220428063043-89078438f170 h1:DiLBVp4DAcZlBVBEtJpNWZpZVq0AEeCY7Hqk8URVs4o= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/opencontainers/selinux v1.12.0 h1:6n5JV4Cf+4y0KNXW48TLj5DwfXpvWlxXplUkdTrmPb8= +github.com/opencontainers/selinux v1.12.0/go.mod h1:BTPX+bjVbWGXw7ZZWUbdENt8w0htPSrlgOOysQaU62U= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= -github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= +github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= +github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= +github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= github.com/pkg/browser v0.0.0-20201207095918-0426ae3fba23/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pkg/sftp v1.13.5 h1:a3RLUqkyjYRtBTZJZ1VRrKbN3zhuPLlUc3sphVz81go= -github.com/pkg/sftp v1.13.5/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pkg/sftp v1.13.9 h1:4NGkvGudBL7GteO3m6qnaQ4pC0Kvf0onSVc9gR3EWBw= +github.com/pkg/sftp v1.13.9/go.mod h1:OBN7bVXdstkFFN/gdnHPUb5TE8eb8G1Rp9wCItqjkkA= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.64.0 h1:pdZeA+g617P7oGv1CzdTzyeShxAGrTBsolKNOLQPGO4= +github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= -github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= -github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= +github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= +github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= +github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k= +github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk= +github.com/schollz/progressbar/v3 v3.18.0 h1:uXdoHABRFmNIjUfte/Ex7WtuyVslrw2wVPQmCN62HpA= +github.com/schollz/progressbar/v3 v3.18.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8GjO0Y9S69eFvNsec= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= -github.com/secure-systems-lab/go-securesystemslib v0.4.0 h1:b23VGrQhTA8cN2CbBw7/FulN9fTtqYUdS5+Oxzt+DUE= -github.com/segmentio/analytics-go/v3 v3.2.1 h1:G+f90zxtc1p9G+WigVyTR0xNfOghOGs/PYAlljLOyeg= -github.com/segmentio/analytics-go/v3 v3.2.1/go.mod h1:p8owAF8X+5o27jmvUognuXxdtqvSGtD0ZrfY2kcS9bE= -github.com/segmentio/backo-go v1.0.0 h1:kbOAtGJY2DqOR0jfRkYEorx/b18RgtepGtY3+Cpe6qA= -github.com/segmentio/backo-go v1.0.0/go.mod h1:kJ9mm9YmoWSkk+oQ+5Cj8DEoRCX2JT6As4kEtIIOp1M= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/secure-systems-lab/go-securesystemslib v0.9.0 h1:rf1HIbL64nUpEIZnjLZ3mcNEL9NBPB0iuVjyxvq3LZc= +github.com/secure-systems-lab/go-securesystemslib v0.9.0/go.mod h1:DVHKMcZ+V4/woA/peqr+L0joiRXbPpQ042GgJckkFgw= +github.com/segmentio/analytics-go/v3 v3.3.0 h1:8VOMaVGBW03pdBrj1CMFfY9o/rnjJC+1wyQHlVxjw5o= +github.com/segmentio/analytics-go/v3 v3.3.0/go.mod h1:p8owAF8X+5o27jmvUognuXxdtqvSGtD0ZrfY2kcS9bE= +github.com/segmentio/backo-go v1.1.0 h1:cJIfHQUdmLsd8t9IXqf5J8SdrOMn9vMa7cIvOavHAhc= +github.com/segmentio/backo-go v1.1.0/go.mod h1:ckenwdf+v/qbyhVdNPWHnqh2YdJBED1O9cidYyM5J18= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI= +github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE= +github.com/shirou/gopsutil/v4 v4.25.5 h1:rtd9piuSMGeU8g1RMXjZs9y9luK5BwtnG7dZaQUJAsc= +github.com/shirou/gopsutil/v4 v4.25.5/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= +github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spdx/tools-golang v0.5.3 h1:ialnHeEYUC4+hkm5vJm4qz2x+oEJbS0mAMFrNXdQraY= +github.com/spdx/tools-golang v0.5.3/go.mod h1:/ETOahiAo96Ob0/RAIBmFZw6XN0yTnyr/uFZm2NTMhI= +github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= +github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= +github.com/spf13/cast v1.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk= +github.com/spf13/cast v1.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU= -github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= +github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= +github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE= +github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -604,113 +1369,165 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= -github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/syncthing/notify v0.0.0-20250528144937-c7027d4f7465 h1:yhxdTGmFkAM2TFA65c3NgGwpnIkUM8oVqPX2e9S7IVg= +github.com/syncthing/notify v0.0.0-20250528144937-c7027d4f7465/go.mod h1:J0q59IWjLtpRIJulohwqEZvjzwOfTEPp8SVhDJl+y0Y= +github.com/syncthing/syncthing v1.29.6 h1:cfryI6OEc6xlckuy/5iCPsSxYj+m7NQuO9pqwhBMp9c= +github.com/syncthing/syncthing v1.29.6/go.mod h1:Rg08aElF6QSPQELa+zgBeWijvvjGXDGIhif2Y6Mxv2o= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/tensorchord/envd-server v0.0.25 h1:r9cthgOmhqJl7keetaAFp9h2s7BeHMf+05vMxENkx4w= -github.com/tensorchord/envd-server v0.0.25/go.mod h1:BmXRR/nVGPCtjaTCe7QVH26R4T+FEKbxJa9LGa7o0GA= -github.com/tonistiigi/fsutil v0.0.0-20230105215944-fb433841cbfa h1:XOFp/3aBXlqmOFAg3r6e0qQjPnK5I970LilqX+Is1W8= -github.com/tonistiigi/fsutil v0.0.0-20230105215944-fb433841cbfa/go.mod h1:AvLEd1LEIl64G2Jpgwo7aVV5lGH0ePcKl0ygGIHNYl8= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= +github.com/tensorchord/envd-server v0.0.27 h1:zCJn4tQNplW0G2dtC9tWCKVry6ZulXjnN//6gPtPePg= +github.com/tensorchord/envd-server v0.0.27/go.mod h1:z73SMAiAvPo+4mY2xnjxUbJJ0ub1ihbyglPEAOng99I= +github.com/thejerf/suture/v4 v4.0.6 h1:QsuCEsCqb03xF9tPAsWAj8QOAJBgQI1c0VqJNaingg8= +github.com/thejerf/suture/v4 v4.0.6/go.mod h1:gu9Y4dXNUWFrByqRt30Rm9/UZ0wzRSt9AJS6xu/ZGxU= +github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= +github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= +github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= +github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ= +github.com/tonistiigi/fsutil v0.0.0-20250417144416-3f76f8130144 h1:k9tdF32oJYwtjzMx+D26M6eYiCaAPdJ7tyN7tF1oU5Q= +github.com/tonistiigi/fsutil v0.0.0-20250417144416-3f76f8130144/go.mod h1:BKdcez7BiVtBvIcef90ZPc6ebqIWr4JWD7+EvLm6J98= +github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 h1:2f304B10LaZdB8kkVEaoXvAMVan2tl9AiK4G0odjQtE= +github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0/go.mod h1:278M4p8WsNh3n4a1eqiFcV2FGk7wE5fwUpUom9mK9lE= github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0= github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk= -github.com/tonistiigi/vt100 v0.0.0-20210615222946-8066bb97264f h1:DLpt6B5oaaS8jyXHa9VA4rrZloBVPVXeCtrOsrFauxc= -github.com/tonistiigi/vt100 v0.0.0-20210615222946-8066bb97264f/go.mod h1:ulncasL3N9uLrVann0m+CDlJKWsIAP34MPcOJF6VRvc= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab h1:H6aJ0yKQ0gF49Qb2z5hI1UHxSQt4JMyxebFR15KnApw= +github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab/go.mod h1:ulncasL3N9uLrVann0m+CDlJKWsIAP34MPcOJF6VRvc= +github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= +github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli/v2 v2.24.1 h1:/QYYr7g0EhwXEML8jO+8OYt5trPnLHS0p3mrgExJ5NU= -github.com/urfave/cli/v2 v2.24.1/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= -github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= -github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= +github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g= +github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= +github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo= +github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= -github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= -github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= -github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= -github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= -github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= +github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM= +github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.29.0 h1:n9b7AAdbQtQ0k9dm0Dm2/KUcUqtG8i2O15KzNaDze8c= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.29.0/go.mod h1:LsankqVDx4W+RhZNA5uWarULII/MBhF5qwCYxTuyXjs= -go.opentelemetry.io/otel v1.4.0/go.mod h1:jeAqMFKy2uLIxCtKxoFj0FAL5zAPKQagc3+GtBWakzk= -go.opentelemetry.io/otel v1.4.1 h1:QbINgGDDcoQUoMJa2mMaWno49lja9sHwp6aoa2n3a4g= -go.opentelemetry.io/otel v1.4.1/go.mod h1:StM6F/0fSwpd8dKWDCdRr7uRvEPYdW0hBSlbdTiUde4= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.1/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.1 h1:WPpPsAAs8I2rA47v5u0558meKmmwm1Dj99ZbqCV8sZ8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.4.1/go.mod h1:o5RW5o2pKpJLD5dNTCmjF1DorYwMeFJmb/rKr5sLaa8= -go.opentelemetry.io/otel/sdk v1.4.1 h1:J7EaW71E0v87qflB4cDolaqq3AcujGrtyIPGQoZOB0Y= -go.opentelemetry.io/otel/sdk v1.4.1/go.mod h1:NBwHDgDIBYjwK2WNu1OPgsIc2IJzmBXNnvIJxJc8BpE= -go.opentelemetry.io/otel/trace v1.4.0/go.mod h1:uc3eRsqDfWs9R7b92xbQbU42/eTNz4N+gLP8qJCi4aE= -go.opentelemetry.io/otel/trace v1.4.1 h1:O+16qcdTrT7zxv2J6GejTPFinSwA++cYerC5iSiF8EQ= -go.opentelemetry.io/otel/trace v1.4.1/go.mod h1:iYEVbroFCNut9QkwEczV9vMRPHNKSSwYZjulEtsmhFc= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/detectors/gcp v1.36.0 h1:F7q2tNlCaHY9nMKHR6XH9/qkp8FktLnIcy6jJNyOCQw= +go.opentelemetry.io/contrib/detectors/gcp v1.36.0/go.mod h1:IbBN8uAIIx734PTonTPxAxnjc2pQTxWNkwfstZ+6H2k= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= +go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0 h1:lREC4C0ilyP4WibDhQ7Gg2ygAQFP8oR07Fst/5cafwI= +go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0/go.mod h1:HfvuU0kW9HewH14VCOLImqKvUgONodURG7Alj/IrnGI= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1xzhKrotaHy3/KXdPhlWARrCgK+eqUY= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw= +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= +go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= +go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= +go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= +go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= +go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= +go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.12.0 h1:CMJ/3Wp7iOWES+CYLfnBv+DVmPbB+kmy9PJ92XvlR6c= -go.opentelemetry.io/proto/otlp v0.12.0/go.mod h1:TsIjwGWIx5VFYv9KGVlOpxoBl5Dy+63SUguV7GGvlSQ= -go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd h1:Uo/x0Ir5vQJ+683GXB9Ug+4fcjsbp7z7Ul8UaZbhsRM= -go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os= +go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo= +go.starlark.net v0.0.0-20250530210732-c81913c6f2e2 h1:N2WAN7QuNpXJWkx/i4LqZ5UP0v+mVwMpPORRJ2INJZc= +go.starlark.net v0.0.0-20250530210732-c81913c6f2e2/go.mod h1:YKMCv9b1WrfWmeqdV5MAuEHWsu5iC+fe6kYl2sQjdI8= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= -golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= +golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -734,14 +1551,23 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -749,7 +1575,6 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -768,16 +1593,44 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -787,7 +1640,28 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= +golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= +golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= +golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -799,14 +1673,22 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -815,9 +1697,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -841,7 +1721,6 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -851,41 +1730,86 @@ golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201113234701-d7a72108b828/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= +golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -895,24 +1819,36 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= +golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -922,6 +1858,7 @@ golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -951,18 +1888,44 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= +golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= +gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= +gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= +gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -982,15 +1945,53 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= +google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= +google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= +google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= +google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= +google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= +google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= +google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= +google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= +google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= +google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= +google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= +google.golang.org/api v0.235.0 h1:C3MkpQSRxS1Jy6AkzTGKKrpSCOd2WOGrezZ+icKSkKo= +google.golang.org/api v0.235.0/go.mod h1:QpeJkemzkFKe5VCE/PMv7GsUfn9ZF+u+q1Q7w6ckxTg= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1012,7 +2013,6 @@ google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= @@ -1028,11 +2028,108 @@ google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e h1:S9GbmC1iCgvbLyAokVCwiO6tVIrU9Y7c5oMx1V/ki/Y= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= +google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= +google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= +google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= +google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= +google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= +google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= +google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= +google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= +google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= +google.golang.org/genproto v0.0.0-20250528174236-200df99c418a h1:KXuwdBmgjb4T3l4ZzXhP6HxxFKXD9FcK5/8qfJI4WwU= +google.golang.org/genproto v0.0.0-20250528174236-200df99c418a/go.mod h1:Nlk93rrS2X7rV8hiC2gh2A/AJspZhElz9Oh2KGsjLEY= +google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a h1:SGktgSolFCo75dnHJF2yMvnns6jCmHFJ0vE4Vn2JKvQ= +google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a/go.mod h1:a77HrdMjoeKbnd2jmgcWdaS++ZLZAEq3orIOAEIKiVw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1052,11 +2149,32 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= +google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= +google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= +google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8= +google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1070,22 +2188,22 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= -gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= -gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= @@ -1096,17 +2214,15 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= +gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1114,6 +2230,42 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= +lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= +modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= +modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= +modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= +modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= +modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= +modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= +modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= +modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= +modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= +modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= +modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= +modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/hack/changelog.sh b/hack/changelog.sh index 55393bba1..5a2cf5509 100755 --- a/hack/changelog.sh +++ b/hack/changelog.sh @@ -9,7 +9,7 @@ git tag -l 'v*' | sort -rV | while read last; do if [ "$tag" != "" ]; then echo "## $(git for-each-ref --format='%(refname:strip=2) (%(creatordate:short))' refs/tags/${tag})" echo - git_log='git --no-pager log --no-merges --invert-grep --grep=^\(build\|ci\|docs\|test\|chore\):' + git_log='git --no-pager log --no-merges --invert-grep --grep=^\(build\|ci\|docs\|test\|chore\|chore(deps)\):' $git_log --format=' * [%h](https://github.com/tensorchord/envd/commit/%H) %s' $last..$tag echo echo "### Contributors" diff --git a/lychee.toml b/lychee.toml new file mode 100644 index 000000000..03c2c701f --- /dev/null +++ b/lychee.toml @@ -0,0 +1,4 @@ +scheme = [ "https", "http", "mailto" ] +exclude_loopback = true +exclude_path = ["CHANGELOG.md"] +accept = ["200..=204", "429", "500..=599"] diff --git a/pkg/app/app.go b/pkg/app/app.go index a547794f2..a5aa1c1d8 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ import ( "github.com/cockroachdb/errors" _ "github.com/moby/buildkit/client/connhelper/dockercontainer" _ "github.com/moby/buildkit/client/connhelper/kubepod" - _ "github.com/moby/buildkit/client/connhelper/nerdctlcontainer" _ "github.com/moby/buildkit/client/connhelper/podmancontainer" "github.com/sirupsen/logrus" "github.com/spf13/viper" @@ -62,7 +61,7 @@ func New() EnvdApp { &cli.StringFlag{ Name: flag.FlagBuildkitdImage, Usage: "docker image to use for buildkitd", - Value: "docker.io/moby/buildkit:v0.10.3", + Value: "docker.io/moby/buildkit:v0.19.0", Hidden: true, }, &cli.StringFlag{ @@ -99,6 +98,8 @@ func New() EnvdApp { CommandDebug, CommandVersion, CommandTop, + CommandReference, + CommandNew, } internalApp.CustomAppHelpTemplate = ` envd - Development environment for data science and AI/ML teams @@ -156,6 +157,15 @@ func New() EnvdApp { return nil } + internalApp.ExitErrHandler = func(context *cli.Context, err error) { + if err != nil { + logrus.WithFields(logrus.Fields{ + "app": "envd", + "version": internalApp.Version, + }).WithError(err).Fatal("exit") + } + } + return EnvdApp{ App: *internalApp, } diff --git a/pkg/app/bootstrap.go b/pkg/app/bootstrap.go index f238ace65..982fb7824 100644 --- a/pkg/app/bootstrap.go +++ b/pkg/app/bootstrap.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ package app import ( + "encoding/json" "fmt" "os" "path/filepath" @@ -29,6 +30,8 @@ import ( "github.com/tensorchord/envd/pkg/buildkitd" "github.com/tensorchord/envd/pkg/home" sshconfig "github.com/tensorchord/envd/pkg/ssh/config" + "github.com/tensorchord/envd/pkg/types" + "github.com/tensorchord/envd/pkg/util/buildkitutil" "github.com/tensorchord/envd/pkg/util/fileutil" ) @@ -45,20 +48,40 @@ var CommandBootstrap = &cli.Command{ }, &cli.BoolFlag{ Name: "with-autocomplete", - Usage: "Add envd autocompletions", + Usage: "Add envd auto-completions", Value: true, }, &cli.StringFlag{ Name: "dockerhub-mirror", - Usage: "Dockerhub mirror to use", + Usage: "DockerHub mirror to use", Aliases: []string{"m"}, }, + &cli.StringFlag{ + Name: "registry-ca-keypair", + Usage: "Specify the ca/key/cert file path for the private registry (format: 'ca=/etc/config/ca.pem,key=/etc/config/key.pem,cert=/etc/config/cert.pem')", + Aliases: []string{"ca"}, + }, &cli.StringSliceFlag{ Name: "ssh-keypair", - Usage: fmt.Sprintf("Manually specify ssh key pair as `publicKey,privateKey`. Envd will generate a keypair at %s and %s if not specified", + Usage: fmt.Sprintf("Manually specify ssh key pair as `publicKey,privateKey`. envd will generate a keypair at %s and %s if not specified", sshconfig.GetPublicKeyOrPanic(), sshconfig.GetPrivateKeyOrPanic()), Aliases: []string{"k"}, }, + &cli.BoolFlag{ + Name: "use-http", + Usage: "Use HTTP instead of HTTPS for the registry", + Value: false, + }, + &cli.StringFlag{ + Name: "registry", + Usage: "Specify the registry to pull the image from", + Aliases: []string{"r"}, + Value: "docker.io", + }, + &cli.StringFlag{ + Name: "registry-config", + Usage: "Path to a JSON file containing registry configuration. Cannot be used with 'registry' or 'registry-ca-keypair'", + }, }, Action: bootstrap, @@ -71,17 +94,27 @@ func bootstrap(clicontext *cli.Context) error { }{{ "SSH Key", sshKey, + }, { + "registry CA keypair", + registryCA, + }, { + "registry json config", + registryJSONConfig, }, { "autocomplete", autocomplete, }, { "buildkit", buildkit, + }, { + "add pre-defined templates", + addTemplates, }} total := len(stages) for i, stage := range stages { - logrus.Infof("[%d/%d] Bootstrap %s", i+1, total, stage.Name) + logrus.WithField("cmd", "bootstrap"). + Infof("[%d/%d] Bootstrap %s", i+1, total, stage.Name) err := stage.Func(clicontext) if err != nil { return err @@ -91,6 +124,134 @@ func bootstrap(clicontext *cli.Context) error { return nil } +func registryCA(clicontext *cli.Context) error { + configFile := clicontext.String("registry-config") + ca := clicontext.String("registry-ca-keypair") + registry := clicontext.String("registry") + + if len(ca) == 0 { + return nil + } + + // We only need this check in registryCA because it is called before registryJSONConfig + if len(configFile) > 0 && len(ca) > 0 { + return errors.New("only one of `registry-config` and `registry-ca-keypair` can be used") + } + + mirror := clicontext.String("dockerhub-mirror") + if len(mirror) == 0 { + return errors.New("`registry-ca-keypair` should be used with `dockerhub-mirror`") + } + + // Parse ca/key/cert + kvPairs := strings.Split(ca, ",") + if len(kvPairs) != 3 { + return errors.New("`registry-ca-keypair` requires ca/key/cert 3 part separated by ','") + } + names := []string{"ca", "cert", "key"} + for _, pair := range kvPairs { + kv := strings.SplitN(pair, "=", 2) + index := -1 + for i, name := range names { + if name == kv[0] { + index = i + break + } + } + if index == -1 { + return errors.Newf("parse error: `%s` is not a valid ca/key/cert key or it's duplicated") + } + exist, err := fileutil.FileExists(kv[1]) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("failed to parse file path %s", pair)) + } + if !exist { + return errors.Newf("file %s doesn't exist", kv[1]) + } + + path, err := fileutil.ConfigFile(fmt.Sprintf("%s_%s.pem", registry, kv[0])) + if err != nil { + return errors.Wrap(err, "failed to get the envd config file path") + } + content, err := os.ReadFile(kv[1]) + if err != nil { + return errors.Wrap(err, "failed to read the file") + } + if err = os.WriteFile(path, content, 0644); err != nil { + return errors.Wrap(err, "failed to store the CA file") + } + names = append(names[:index], names[index+1:]...) + } + + if len(names) != 0 { + return errors.Newf("registry %s are not provided", names) + } + return nil +} + +func registryJSONConfig(clicontext *cli.Context) error { + configFile := clicontext.String("registry-config") + if len(configFile) == 0 { + return nil + } + + // Check if file exists + exist, err := fileutil.FileExists(configFile) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("failed to parse file path %s", configFile)) + } + if !exist { + return errors.Newf("file %s doesn't exist", configFile) + } + + config := buildkitutil.BuildkitConfig{} + configJson, err := os.ReadFile(configFile) + if err != nil { + return errors.Wrap(err, "Failed to read registry config file") + } + if err := json.Unmarshal(configJson, &config); err != nil { + return errors.Wrap(err, "Failed to parse registry config file") + } + + // Check for required keys in each registry + for i, registry := range config.Registries { + if registry.Name == "" { + return errors.Newf("`name` key is required in the config for registry at index %d", i) + } + + // Check for optional keys and if they exist, ensure they point to existing files + optionalKeys := map[string]string{"ca": registry.Ca, "cert": registry.Cert, "key": registry.Key} + for key, value := range optionalKeys { + if value != "" { + exist, err := fileutil.FileExists(value) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("failed to parse file path %s", value)) + } + if !exist { + return errors.Newf("file %s doesn't exist", value) + } + + // Read the file + content, err := os.ReadFile(value) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("failed to read the %s file for registry %s", key, registry.Name)) + } + + // Write the content to the envd config directory + envdConfigPath, err := fileutil.ConfigFile(fmt.Sprintf("%s_%s.pem", registry.Name, key)) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("failed to get the envd config file path for %s of registry %s", key, registry.Name)) + } + + if err = os.WriteFile(envdConfigPath, content, 0644); err != nil { + return errors.Wrap(err, fmt.Sprintf("failed to store the %s file for registry %s", key, registry.Name)) + } + } + } + } + return nil +} + func sshKey(clicontext *cli.Context) error { sshKeyPair := clicontext.StringSlice("ssh-keypair") @@ -116,7 +277,7 @@ func sshKey(clicontext *cli.Context) error { return errors.Wrap(err, "Cannot get default key status") } - path, err := sshconfig.GetPrivateKey() + privatePath, err := sshconfig.GetPrivateKey() if err != nil { return errors.Wrap(err, "Cannot get private key path") } @@ -126,16 +287,22 @@ func sshKey(clicontext *cli.Context) error { var newPrivateKeyName string for ok := true; ok; ok = exists { - newPrivateKeyName = filepath.Join(filepath.Dir(path), + newPrivateKeyName = filepath.Join(filepath.Dir(privatePath), fmt.Sprintf("envd_%s.pk", namesgenerator.GetRandomName(0))) exists, err = fileutil.FileExists(newPrivateKeyName) if err != nil { return err } } - logrus.Debugf("New key name: %s", newPrivateKeyName) + + logrus.WithFields(logrus.Fields{ + "cmd": "bootstrap", + "stage": "sshKey", + "sshKeyPair": sshKeyPair, + }).Debugf("New key name: %s", newPrivateKeyName) + if err := sshconfig.ReplaceKeyManagedByEnvd( - path, newPrivateKeyName); err != nil { + privatePath, newPrivateKeyName); err != nil { return err } } @@ -144,7 +311,11 @@ func sshKey(clicontext *cli.Context) error { if err != nil { return errors.Wrap(err, "Cannot open public key") } - err = os.WriteFile(path, pubKey, 0644) + publicPath, err := sshconfig.GetPublicKey() + if err != nil { + return errors.Wrap(err, "Cannot get the public key path") + } + err = os.WriteFile(publicPath, pubKey, 0644) if err != nil { return errors.Wrap(err, "Cannot write public key") } @@ -154,14 +325,14 @@ func sshKey(clicontext *cli.Context) error { return errors.Wrap(err, "Cannot open private key") } - err = os.WriteFile(path, priKey, 0600) + err = os.WriteFile(privatePath, priKey, 0600) if err != nil { return errors.Wrap(err, "Cannot write private key") } return nil default: - return errors.Errorf("Invalid ssh-keypair flag") + return errors.Newf("Invalid ssh-keypair flag") } } @@ -172,24 +343,36 @@ func autocomplete(clicontext *cli.Context) error { } shell := os.Getenv("SHELL") + + logger := logrus.WithFields(logrus.Fields{ + "cmd": "bootstrap", + "stage": "autocomplete", + "shell": shell, + }) + if strings.Contains(shell, "zsh") { - logrus.Infof("Install zsh autocompletion") - if err := ac.InsertZSHCompleteEntry(); err != nil { - logrus.Warnf("Warning: %s\n", err.Error()) + logger.Infof("Install zsh autocompletion") + if err := ac.InsertZSHCompleteEntry(clicontext); err != nil { + logger.WithError(err).Warn() } } else if strings.Contains(shell, "bash") { - logrus.Infof("Install bash autocompletion") - if err := ac.InsertBashCompleteEntry(); err != nil { - logrus.Warnf("Warning: %s\n", err.Error()) + logger.Infof("Install bash autocompletion") + if err := ac.InsertBashCompleteEntry(clicontext); err != nil { + logger.WithError(err).Warn() + } + } else if strings.Contains(shell, "fish") { + logger.Infof("Install fish autocompletion") + if err := ac.InsertFishCompleteEntry(clicontext); err != nil { + logger.WithError(err).Warn() } } else { - logrus.Infof("Install bash autocompletion (fallback from \"%s\")", shell) - if err := ac.InsertBashCompleteEntry(); err != nil { - logrus.Warnf("Warning: %s\n", err.Error()) + logger.Infof(`Install bash autocompletion (fallback from "%s")`, shell) + if err := ac.InsertBashCompleteEntry(clicontext); err != nil { + logger.WithError(err).Warn() } } - logrus.Info("You may have to restart your shell for autocomplete to get initialized (e.g. run \"exec $SHELL\")\n") + logger.Info(`You may have to restart your shell for autocomplete to get initialized (e.g. run "exec $SHELL")\n`) return nil } @@ -204,14 +387,60 @@ func buildkit(clicontext *cli.Context) error { return errors.Wrap(err, "failed to get the current context") } - logrus.Debug("bootstrap the buildkitd container") - bkClient, err := buildkitd.NewClient(clicontext.Context, - c.Builder, c.BuilderAddress, clicontext.String("dockerhub-mirror")) - if err != nil { - return errors.Wrap(err, "failed to create buildkit client") + logger := logrus.WithFields(logrus.Fields{ + "cmd": "bootstrap", + "stage": "buildkit", + }) + + logger.Debug("bootstrap the buildkitd container") + // Populate the BuildkitConfig struct + config := buildkitutil.BuildkitConfig{} + + configFile := clicontext.String("registry-config") + if len(configFile) != 0 { + configJson, err := os.ReadFile(configFile) + if err != nil { + return errors.Wrap(err, "Failed to read registry config file") + } + if err := json.Unmarshal(configJson, &config); err != nil { + return errors.Wrap(err, "Failed to parse registry config file") + } + } else if len(clicontext.String("registry-ca-keypair")) != 0 { + // The values of Ca, Cert, and Key don't actually matter since we already copied their contents to the envd config directory and mounted to `/etc/registry`. + // So instead of parsing registry-ca-keypair again, we'll just put the default value. + // This is to ensure that buildkitConfigTemplate parses properly. + config.Registries = append(config.Registries, buildkitutil.Registry{ + Name: clicontext.String("registry"), + Ca: "/etc/registry", + Cert: "/etc/registry", + Key: "/etc/registry", + UseHTTP: clicontext.Bool("use-http"), + Mirror: clicontext.String("dockerhub-mirror"), + }) + } else if len(clicontext.String("dockerhub-mirror")) != 0 { + config.Registries = append(config.Registries, buildkitutil.Registry{ + Name: clicontext.String("registry"), + UseHTTP: clicontext.Bool("use-http"), + Mirror: clicontext.String("dockerhub-mirror"), + }) + } + + var bkClient buildkitd.Client + if c.Builder == types.BuilderTypeMoby { + bkClient, err = buildkitd.NewMobyClient(clicontext.Context, + c.Builder, c.BuilderAddress, &config) + if err != nil { + return errors.Wrap(err, "failed to create moby buildkit client") + } + } else { + bkClient, err = buildkitd.NewClient(clicontext.Context, + c.Builder, c.BuilderAddress, &config) + if err != nil { + return errors.Wrap(err, "failed to create buildkit client") + } } defer bkClient.Close() - logrus.Infof("The buildkit is running at %s", bkClient.BuildkitdAddr()) + logger.Infof("The buildkit is running at %s", bkClient.BuildkitdAddr()) return nil } diff --git a/pkg/app/build.go b/pkg/app/build.go index d7d8cf0cc..2e08218a4 100644 --- a/pkg/app/build.go +++ b/pkg/app/build.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ package app import ( + "strings" "time" "github.com/sirupsen/logrus" @@ -23,6 +24,7 @@ import ( buildutil "github.com/tensorchord/envd/pkg/app/build" "github.com/tensorchord/envd/pkg/app/telemetry" sshconfig "github.com/tensorchord/envd/pkg/ssh/config" + "github.com/tensorchord/envd/pkg/util/runtimeutil" ) var CommandBuild = &cli.Command{ @@ -70,7 +72,7 @@ To build and push the image to a registry: }, &cli.StringFlag{ Name: "output", - Usage: "Output destination (e.g. type=tar,dest=path,push=true)", + Usage: "Output destination (e.g. `type=tar,dest=path,push=true`)", Aliases: []string{"o"}, }, &cli.BoolFlag{ @@ -81,14 +83,20 @@ To build and push the image to a registry: // https://github.com/urfave/cli/issues/1134#issuecomment-1191407527 &cli.StringFlag{ Name: "export-cache", - Usage: "Export the cache (e.g. type=registry,ref=<image>)", + Usage: "Export the cache (e.g. `type=registry,ref=<image>`). The default `moby-worker` builder doesn't support this unless the docker-ce has enabled the `containerd` image store. You can run `envd context create --name docker --builder docker-container --use` to use this feature.", Aliases: []string{"ec"}, }, &cli.StringFlag{ Name: "import-cache", - Usage: "Import the cache (e.g. type=registry,ref=<image>)", + Usage: "Import the cache (e.g. `type=registry,ref=<image>`)", Aliases: []string{"ic"}, }, + &cli.StringFlag{ + Name: "platform", + Usage: `Specify the target platforms for the build output (for example, "windows/amd64" or "linux/amd64,darwin/arm64"). +Build images with same tags could cause image overwriting, platform suffixes will be added to differentiate the images.`, + DefaultText: runtimeutil.GetRuntimePlatform(), + }, }, Action: build, } @@ -103,15 +111,30 @@ func build(clicontext *cli.Context) error { "build", telemetry.AddField("duration", time.Since(start).Seconds())) }(time.Now()) - logger := logrus.WithField("builder-options", opt) + logger := logrus.WithFields(logrus.Fields{ + "cmd": "build", + "builder-options": opt, + }) logger.Debug("starting build command") - builder, err := buildutil.GetBuilder(clicontext, opt) - if err != nil { - return err - } - if err = buildutil.InterpretEnvdDef(builder); err != nil { - return err + platforms := strings.Split(opt.Platform, ",") + for _, platform := range platforms { + o := opt + o.Platform = platform + if len(platforms) > 1 { + // Transform the platform suffix to comply with the tag naming rule. + o.Tag += "-" + strings.Replace(platform, "/", "-", 1) + } + builder, err := buildutil.GetBuilder(clicontext, o) + if err != nil { + return err + } + if err = buildutil.InterpretEnvdDef(builder); err != nil { + return err + } + if err := buildutil.BuildImage(clicontext, builder); err != nil { + return err + } } - return buildutil.BuildImage(clicontext, builder) + return nil } diff --git a/pkg/app/build/build.go b/pkg/app/build/build.go index c7749f1e7..6d0423501 100644 --- a/pkg/app/build/build.go +++ b/pkg/app/build/build.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ import ( "github.com/tensorchord/envd/pkg/driver/docker" "github.com/tensorchord/envd/pkg/envd" "github.com/tensorchord/envd/pkg/home" + progressmode "github.com/tensorchord/envd/pkg/progress/mode" "github.com/tensorchord/envd/pkg/util/fileutil" ) @@ -134,6 +135,7 @@ func ParseBuildOpt(clicontext *cli.Context) (builder.Options, error) { exportCache := clicontext.String("export-cache") importCache := clicontext.String("import-cache") useProxy := clicontext.Bool("use-proxy") + platform := clicontext.String("platform") opt := builder.Options{ ManifestFilePath: manifest, @@ -143,15 +145,16 @@ func ParseBuildOpt(clicontext *cli.Context) (builder.Options, error) { Tag: tag, OutputOpts: output, PubKeyPath: clicontext.Path("public-key"), - ProgressMode: "auto", + ProgressMode: progressmode.AUTO, ExportCache: exportCache, ImportCache: importCache, UseHTTPProxy: useProxy, + Platform: platform, } debug := clicontext.Bool("debug") if debug { - opt.ProgressMode = "plain" + opt.ProgressMode = progressmode.PLAIN } return opt, nil } diff --git a/pkg/app/completion.go b/pkg/app/completion.go index bd92adc95..c5249ad2a 100644 --- a/pkg/app/completion.go +++ b/pkg/app/completion.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -45,15 +45,15 @@ var CommandCompletion = &cli.Command{ Action: completion, } -func handleCompletion(clicontext *cli.Context, installFunc func() error, outputFunc func() (string, error)) error { +func handleCompletion(clicontext *cli.Context, installFunc func(*cli.Context) error, outputFunc func(*cli.Context) (string, error)) error { if clicontext.Bool("no-install") { - script, err := outputFunc() + script, err := outputFunc(clicontext) if err != nil { return err } fmt.Println(script) } else { - if err := installFunc(); err != nil { + if err := installFunc(clicontext); err != nil { return err } } @@ -75,7 +75,8 @@ func completion(clicontext *cli.Context) error { } for i := 0; i < n; i++ { - logrus.Infof("[%d/%d] Add completion %s", i+1, n, shellList[i]) + logrus.WithField("cmd", "completion"). + Infof("[%d/%d] Add completion %s", i+1, n, shellList[i]) switch shellList[i] { case "zsh": if err := handleCompletion(clicontext, ac.InsertZSHCompleteEntry, ac.ZshCompleteEntry); err != nil { @@ -85,6 +86,10 @@ func completion(clicontext *cli.Context) error { if err := handleCompletion(clicontext, ac.InsertBashCompleteEntry, ac.BashCompleteEntry); err != nil { return err } + case "fish": + if err := handleCompletion(clicontext, ac.InsertFishCompleteEntry, ac.FishCompleteEntry); err != nil { + return err + } default: return errors.Errorf("unknown shell type %s (support type: {bash|zsh})", shellList[i]) } diff --git a/pkg/app/const.go b/pkg/app/const.go index 613448034..c230b7647 100644 --- a/pkg/app/const.go +++ b/pkg/app/const.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/app/context.go b/pkg/app/context.go index 87fa3feb2..5d2bba7d4 100644 --- a/pkg/app/context.go +++ b/pkg/app/context.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/app/context_create.go b/pkg/app/context_create.go index bbcf6ef9e..6f541953e 100644 --- a/pkg/app/context_create.go +++ b/pkg/app/context_create.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ var CommandContextCreate = &cli.Command{ }, &cli.StringFlag{ Name: "builder", - Usage: "Builder to use (docker-container, kube-pod, tcp, unix)", + Usage: "Builder to use (docker-container, kube-pod, tcp, unix, moby-worker, nerdctl-container)", Value: string(types.BuilderTypeDocker), }, &cli.StringFlag{ @@ -68,6 +68,16 @@ func contextCreate(clicontext *cli.Context) error { runnerAddress := clicontext.String("runner-address") use := clicontext.Bool("use") + logger := logrus.WithFields(logrus.Fields{ + "cmd": "context create", + "name": name, + "builder": builder, + "builderAddress": builderAddress, + "runner": runner, + "runnerAddress": runnerAddress, + "use": use, + }) + c := types.Context{ Name: name, Builder: types.BuilderType(builder), @@ -82,9 +92,9 @@ func contextCreate(clicontext *cli.Context) error { if err != nil { return errors.Wrap(err, "failed to create context") } - logrus.Infof("Context %s is created", name) + logger.Infof("Context %s is created", name) if use { - logrus.Infof("Current context is now \"%s\"", name) + logger.Infof(`Current context is "%s"`, name) } return nil } diff --git a/pkg/app/context_ls.go b/pkg/app/context_ls.go index df1bb70eb..c4602fd69 100644 --- a/pkg/app/context_ls.go +++ b/pkg/app/context_ls.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -43,7 +43,7 @@ func contextList(clicontext *cli.Context) error { format := clicontext.String("format") switch format { case "table": - table.RenderContext(os.Stdout, contexts) + return table.RenderContext(os.Stdout, contexts) case "json": return json.PrintContext(contexts) } diff --git a/pkg/app/context_rm.go b/pkg/app/context_rm.go index 5b749c686..407ea56f9 100644 --- a/pkg/app/context_rm.go +++ b/pkg/app/context_rm.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -43,6 +43,7 @@ func contextRemove(clicontext *cli.Context) error { if err != nil { return errors.Wrap(err, "failed to remove context") } - logrus.Infof("Context %s is removed", name) + logrus.WithField("cmd", "context remove"). + Infof("Context %s is removed", name) return nil } diff --git a/pkg/app/context_use.go b/pkg/app/context_use.go index 941e09750..5314c05a2 100644 --- a/pkg/app/context_use.go +++ b/pkg/app/context_use.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -43,6 +43,7 @@ func contextUse(clicontext *cli.Context) error { if err != nil { return errors.Wrapf(err, "failed to use the specified context %s", name) } - logrus.Infof("Current context is now \"%s\"", name) + logrus.WithField("cmd", "context use"). + Infof(`Current context is "%s"`, name) return nil } diff --git a/pkg/app/debug.go b/pkg/app/debug.go index 7db0cefb0..297d51467 100644 --- a/pkg/app/debug.go +++ b/pkg/app/debug.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -21,7 +21,6 @@ import ( var CommandDebug = &cli.Command{ Name: "debug", Category: CategoryOther, - Aliases: []string{"b"}, Usage: "Debug commands", Description: ``, diff --git a/pkg/app/debug_llb.go b/pkg/app/debug_llb.go index d5f823001..1e699d775 100644 --- a/pkg/app/debug_llb.go +++ b/pkg/app/debug_llb.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -77,6 +77,7 @@ func debugLLB(clicontext *cli.Context) error { } logger := logrus.WithFields(logrus.Fields{ + "cmd": "debug llb", "build-context": opt.BuildContextDir, "build-file": opt.ManifestFilePath, "config": opt.ConfigFilePath, @@ -118,9 +119,9 @@ func debugLLB(clicontext *cli.Context) error { } type llbOp struct { - Op pb.Op + Op *pb.Op Digest digest.Digest - OpMetadata pb.OpMetadata + OpMetadata *pb.OpMetadata } // Refer to https://github.com/moby/buildkit/blob/master/cmd/buildctl/debug/dumpllb.go#L17:5 @@ -128,11 +129,11 @@ func loadLLB(def *llb.Definition) ([]llbOp, error) { var ops []llbOp for _, dt := range def.Def { var op pb.Op - if err := (&op).Unmarshal(dt); err != nil { + if err := (&op).UnmarshalVT(dt); err != nil { return nil, errors.Wrap(err, "failed to parse op") } dgst := digest.FromBytes(dt) - ent := llbOp{Op: op, Digest: dgst, OpMetadata: def.Metadata[dgst]} + ent := llbOp{Op: &op, Digest: dgst, OpMetadata: def.Metadata[dgst].ToPB()} ops = append(ops, ent) } return ops, nil @@ -161,12 +162,12 @@ func writeDot(ops []llbOp, w io.Writer) { } } -func attr(dgst digest.Digest, op pb.Op) (string, string) { +func attr(dgst digest.Digest, op *pb.Op) (string, string) { switch op := op.Op.(type) { case *pb.Op_Source: return op.Source.Identifier, "ellipse" case *pb.Op_Exec: - return generateExecNode(op.Exec) + return strings.Join(op.Exec.Meta.Args, " "), "box" case *pb.Op_Build: return "build", "box3d" case *pb.Op_Merge: @@ -197,26 +198,3 @@ func attr(dgst digest.Digest, op pb.Op) (string, string) { return dgst.String(), "plaintext" } } - -func generateExecNode(op *pb.ExecOp) (string, string) { - mounts := []string{} - for _, m := range op.Mounts { - mstr := fmt.Sprintf("selector=%s, target=%s, mount-type=%s", m.Selector, - m.Dest, m.MountType) - if m.CacheOpt != nil { - mstr = mstr + fmt.Sprintf(" cache-id=%s, cache-share-mode = %s", - m.CacheOpt.ID, m.CacheOpt.Sharing) - } - mounts = append(mounts, mstr) - } - - name := fmt.Sprintf("user=%s, cwd=%s, args={%s}, mounts={%s}, env={%s}", - op.Meta.User, - op.Meta.Cwd, - strings.Join(op.Meta.Args, " "), - strings.Join(mounts, " "), - strings.Join(op.Meta.Env, " "), - ) - - return name, "box" -} diff --git a/pkg/app/destroy.go b/pkg/app/destroy.go index 6e6870345..3199dc6ac 100644 --- a/pkg/app/destroy.go +++ b/pkg/app/destroy.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,9 +15,13 @@ package app import ( + "bufio" + "fmt" + "os" "path/filepath" "github.com/cockroachdb/errors" + "github.com/mattn/go-isatty" "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" @@ -26,6 +30,7 @@ import ( "github.com/tensorchord/envd/pkg/envd" "github.com/tensorchord/envd/pkg/home" sshconfig "github.com/tensorchord/envd/pkg/ssh/config" + "github.com/tensorchord/envd/pkg/syncthing" ) var CommandDestroy = &cli.Command{ @@ -50,19 +55,43 @@ var CommandDestroy = &cli.Command{ Action: destroy, } +// Prompts the user to confirm an operation with [Y/n]. +// If the output is not tty, it will return false automatically. +func confirm(prompt string) bool { + isTerminal := isatty.IsTerminal(os.Stdout.Fd()) + if !isTerminal { + return false + } + + reader := bufio.NewReader(os.Stdin) + fmt.Printf("%s [Y/n] ", prompt) + response, err := reader.ReadString('\n') + if err != nil { + return false + } + + response = response[:len(response)-1] // Remove newline character + // default is "Yes" + return response == "y" || response == "Y" || response == "yes" || response == "Yes" || response == "" +} + func destroy(clicontext *cli.Context) error { path := clicontext.Path("path") name := clicontext.String("name") if path != "" && name != "" { return errors.New("Cannot specify --path and --name at the same time.") } - if path == "" && name == "" { - path = "." - } + + logger := logrus.WithFields(logrus.Fields{ + "cmd": "destroy", + "path": path, + "name": name, + }) + var ctrName string if name != "" { ctrName = name - } else { + } else if path != "" { buildContext, err := filepath.Abs(path) if err != nil { return errors.Wrap(err, "failed to get absolute path of the build context") @@ -71,7 +100,22 @@ func destroy(clicontext *cli.Context) error { if err != nil { return errors.Wrap(err, "failed to create an env name") } + } else { + // Both path and name are empty + // Destroy the environment in the current directory only if user confirms + buildContext, err := filepath.Abs(".") + if err != nil { + return errors.Wrap(err, "failed to get absolute path of the build context") + } + ctrName, err = buildutil.CreateEnvNameFromDir(buildContext) + if err != nil { + return errors.Wrap(err, "failed to create an env name") + } + if !confirm(fmt.Sprintf("Are you sure you want to destroy container `%s` in the current directory?", ctrName)) { + return nil + } } + context, err := home.GetManager().ContextGetCurrent() if err != nil { return errors.Wrap(err, "failed to get the current context") @@ -89,12 +133,19 @@ func destroy(clicontext *cli.Context) error { if ctrName, err := envdEngine.Destroy(clicontext.Context, ctrName); err != nil { return errors.Wrapf(err, "failed to destroy the environment: %s", ctrName) } else if ctrName != "" { - logrus.Infof("environment(%s) is destroyed", ctrName) + logger.Infof("environment(%s) is destroyed", ctrName) } if err = sshconfig.RemoveEntry(ctrName); err != nil { - logrus.Infof("failed to remove entry %s from your SSH config file: %s", ctrName, err) + logger.WithError(err). + Infof("failed to remove entry %s from your SSH config file", ctrName) return errors.Wrap(err, "failed to remove entry from your SSH config file") } + + err = syncthing.CleanLocalConfig(name) + if err != nil { + return errors.Wrap(err, "failed to remove syncthing config file") + } + return nil } diff --git a/pkg/app/env.go b/pkg/app/env.go index 7fd68500d..41d17382d 100644 --- a/pkg/app/env.go +++ b/pkg/app/env.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/app/env_describe.go b/pkg/app/env_describe.go index 9b3f500f6..768630c4f 100644 --- a/pkg/app/env_describe.go +++ b/pkg/app/env_describe.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -78,8 +78,11 @@ func getEnvironmentDescriptions(clicontext *cli.Context) error { format := clicontext.String("format") switch format { case "table": - table.RenderDependencies(os.Stdout, dep) - table.RenderPortBindings(os.Stdout, ports) + err = table.RenderDependencies(os.Stdout, dep) + if err != nil { + return err + } + return table.RenderPortBindings(os.Stdout, ports) case "json": return json.PrintEnvironmentDescriptions(dep, ports) } diff --git a/pkg/app/env_ls.go b/pkg/app/env_ls.go index 76d80f382..7683cb52d 100644 --- a/pkg/app/env_ls.go +++ b/pkg/app/env_ls.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -59,7 +59,7 @@ func getEnvironment(clicontext *cli.Context) error { format := clicontext.String("format") switch format { case "table": - table.RenderEnvironments(os.Stdout, envs) + return table.RenderEnvironments(os.Stdout, envs) case "json": return json.PrintEnvironments(envs) } diff --git a/pkg/app/exec.go b/pkg/app/exec.go index a6752aea7..53bc6e2e1 100644 --- a/pkg/app/exec.go +++ b/pkg/app/exec.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -111,12 +111,19 @@ func exec(clicontext *cli.Context) error { return errors.Wrapf(err, "failed to get environment: %s", name) } - logrus.Debugf("runtime commands: %s", rg.RuntimeCommands) - if cmd, ok := rg.RuntimeCommands[command]; !ok { + logrus.WithFields(logrus.Fields{ + "cmd": "exec", + "name": name, + "command": command, + "rawCommand": rawCommand, + "path": path, + }).Debugf("runtime commands: %s", rg.RuntimeCommands) + + cmd, ok := rg.RuntimeCommands[command] + if !ok { return errors.Newf("command %s does not exist", command) - } else { - resultCommand = cmd } + resultCommand = cmd } opt, err := ssh.GetOptions(name) @@ -128,12 +135,12 @@ func exec(clicontext *cli.Context) error { if err != nil { return errors.Wrap(err, "failed to get the ssh client") } - if bytes, err := sshClient.ExecWithOutput(resultCommand); err != nil { + bytes, err := sshClient.ExecWithOutput(resultCommand) + if err != nil { fmt.Fprintln(clicontext.App.Writer, string(bytes)) return errors.Wrapf(err, "failed to execute the command `%s`", resultCommand) - } else { - fmt.Fprint(clicontext.App.Writer, string(bytes)) } + fmt.Fprint(clicontext.App.Writer, string(bytes)) return nil } diff --git a/pkg/app/formatter/data.go b/pkg/app/formatter/data.go index b68c4afcd..a77a87a25 100644 --- a/pkg/app/formatter/data.go +++ b/pkg/app/formatter/data.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/app/formatter/json/context.go b/pkg/app/formatter/json/context.go index 3866d6d08..ecb7a610b 100644 --- a/pkg/app/formatter/json/context.go +++ b/pkg/app/formatter/json/context.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/app/formatter/json/env.go b/pkg/app/formatter/json/env.go index 267b5575f..de9fbe2a4 100644 --- a/pkg/app/formatter/json/env.go +++ b/pkg/app/formatter/json/env.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/app/formatter/json/image.go b/pkg/app/formatter/json/image.go index b46dbed87..ce6172949 100644 --- a/pkg/app/formatter/json/image.go +++ b/pkg/app/formatter/json/image.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/app/formatter/json/print.go b/pkg/app/formatter/json/print.go index 275b653fc..21b2f5a45 100644 --- a/pkg/app/formatter/json/print.go +++ b/pkg/app/formatter/json/print.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/app/formatter/json/version.go b/pkg/app/formatter/json/version.go index f53dac33a..d3bbd95cf 100644 --- a/pkg/app/formatter/json/version.go +++ b/pkg/app/formatter/json/version.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/app/formatter/table/context.go b/pkg/app/formatter/table/context.go index 46d4421b3..cb24da660 100644 --- a/pkg/app/formatter/table/context.go +++ b/pkg/app/formatter/table/context.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,27 +18,15 @@ import ( "fmt" "io" - "github.com/olekukonko/tablewriter" + "github.com/cockroachdb/errors" "github.com/tensorchord/envd/pkg/app/formatter" "github.com/tensorchord/envd/pkg/types" ) -func RenderContext(w io.Writer, contexts types.EnvdContext) { - table := tablewriter.NewWriter(w) - table.SetHeader([]string{"context", "builder", "builder addr", "runner", "runner addr"}) - - table.SetAutoWrapText(false) - table.SetAutoFormatHeaders(true) - table.SetHeaderAlignment(tablewriter.ALIGN_LEFT) - table.SetAlignment(tablewriter.ALIGN_LEFT) - table.SetCenterSeparator("") - table.SetColumnSeparator("") - table.SetRowSeparator("") - table.SetHeaderLine(false) - table.SetBorder(false) - table.SetTablePadding("\t") // pad with tabs - table.SetNoWhiteSpace(true) +func RenderContext(w io.Writer, contexts types.EnvdContext) error { + table := CreateTable(w) + table.Header([]string{"context", "builder", "builder addr", "runner", "runner addr"}) for _, p := range contexts.Contexts { envRow := make([]string, 5) @@ -53,7 +41,10 @@ func RenderContext(w io.Writer, contexts types.EnvdContext) { if p.RunnerAddress != nil { envRow[4] = formatter.StringOrNone(*p.RunnerAddress) } - table.Append(envRow) + err := table.Append(envRow) + if err != nil { + return errors.Wrapf(err, "failed to append row for context %s", p.Name) + } } - table.Render() + return errors.Wrap(table.Render(), "failed to render context table") } diff --git a/pkg/app/formatter/table/env.go b/pkg/app/formatter/table/env.go index 30321f913..b1ffc18c8 100644 --- a/pkg/app/formatter/table/env.go +++ b/pkg/app/formatter/table/env.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -19,31 +19,22 @@ import ( "io" "strconv" + "github.com/cockroachdb/errors" "github.com/olekukonko/tablewriter" + "github.com/olekukonko/tablewriter/renderer" + "github.com/olekukonko/tablewriter/tw" "github.com/tensorchord/envd/pkg/app/formatter" "github.com/tensorchord/envd/pkg/types" ) -func RenderEnvironments(w io.Writer, envs []types.EnvdEnvironment) { - table := tablewriter.NewWriter(w) - table.SetHeader([]string{ +func RenderEnvironments(w io.Writer, envs []types.EnvdEnvironment) error { + table := CreateTable(w) + table.Header([]string{ "Name", "Endpoint", "SSH Target", "Image", "GPU", "CUDA", "CUDNN", "Status", }) - table.SetAutoWrapText(false) - table.SetAutoFormatHeaders(true) - table.SetHeaderAlignment(tablewriter.ALIGN_LEFT) - table.SetAlignment(tablewriter.ALIGN_LEFT) - table.SetCenterSeparator("") - table.SetColumnSeparator("") - table.SetRowSeparator("") - table.SetHeaderLine(false) - table.SetBorder(false) - table.SetTablePadding("\t") // pad with tabs - table.SetNoWhiteSpace(true) - for _, env := range envs { envRow := make([]string, 9) envRow[0] = env.Name @@ -54,16 +45,20 @@ func RenderEnvironments(w io.Writer, envs []types.EnvdEnvironment) { envRow[5] = formatter.StringOrNone(env.CUDA) envRow[6] = formatter.StringOrNone(env.CUDNN) envRow[7] = env.Status.Phase - table.Append(envRow) + err := table.Append(envRow) + if err != nil { + return errors.Wrapf(err, "failed to append row for environment %s", env.Name) + } } - table.Render() + return errors.Wrap(table.Render(), "failed to render environment table") } -func RenderPortBindings(w io.Writer, ports []types.PortBinding) { +func RenderPortBindings(w io.Writer, ports []types.PortBinding) error { if ports == nil { - return + return nil } - table := createTable(w, []string{"Name", "Container Port", "Protocol", "Host IP", "Host Port"}) + table := CreateTable(w) + table.Header([]string{"Name", "Container Port", "Protocol", "Host IP", "Host Port"}) for _, port := range ports { row := make([]string, 5) row[0] = port.Name @@ -71,46 +66,76 @@ func RenderPortBindings(w io.Writer, ports []types.PortBinding) { row[2] = port.Protocol row[3] = port.HostIP row[4] = port.HostPort - table.Append(row) + err := table.Append(row) + if err != nil { + return errors.Wrapf(err, "failed to append row for port binding %s", port.Name) + } } - table.Render() + return errors.Wrap(table.Render(), "failed to render port bindings table") } -func RenderDependencies(w io.Writer, dep *types.Dependency) { +func RenderDependencies(w io.Writer, dep *types.Dependency) error { if dep == nil { - return + return nil } - table := createTable(w, []string{"Dependencies", "Type"}) + table := CreateTable(w) + table.Header([]string{"Dependencies", "Type"}) for _, p := range dep.PyPIPackages { envRow := make([]string, 2) envRow[0] = p envRow[1] = "Python" - table.Append(envRow) + err := table.Append(envRow) + if err != nil { + return errors.Wrapf(err, "failed to append row for Python package %s", p) + } } for _, p := range dep.APTPackages { envRow := make([]string, 2) envRow[0] = p envRow[1] = "APT" - table.Append(envRow) + err := table.Append(envRow) + if err != nil { + return errors.Wrapf(err, "failed to append row for APT package %s", p) + } } - table.Render() + return errors.Wrap(table.Render(), "failed to render dependencies table") } -func createTable(w io.Writer, headers []string) *tablewriter.Table { - table := tablewriter.NewWriter(w) - table.SetHeader(headers) - - table.SetAutoWrapText(false) - table.SetAutoFormatHeaders(true) - table.SetHeaderAlignment(tablewriter.ALIGN_LEFT) - table.SetAlignment(tablewriter.ALIGN_LEFT) - table.SetCenterSeparator("") - table.SetColumnSeparator("") - table.SetRowSeparator("") - table.SetHeaderLine(false) - table.SetBorder(true) - table.SetTablePadding("\t") // pad with tabs - table.SetNoWhiteSpace(true) +func CreateTable(w io.Writer) *tablewriter.Table { + table := tablewriter.NewTable( + w, + tablewriter.WithRowAutoWrap(tw.WrapNone), + tablewriter.WithHeaderAutoFormat(tw.On), + tablewriter.WithRenderer(renderer.NewBlueprint( + tw.Rendition{ + Borders: tw.BorderNone, + Symbols: tw.NewSymbols(tw.StyleNone), + Settings: tw.Settings{ + Separators: tw.Separators{ + BetweenRows: tw.Off, + BetweenColumns: tw.Off, + }, + Lines: tw.Lines{ + ShowHeaderLine: tw.Off, + }, + }, + }, + )), + tablewriter.WithConfig( + tablewriter.Config{ + Header: tw.CellConfig{ + Alignment: tw.CellAlignment{ + Global: tw.AlignLeft, + }, + }, + Row: tw.CellConfig{ + Alignment: tw.CellAlignment{ + Global: tw.AlignLeft, + }, + }, + }, + ), + ) return table } diff --git a/pkg/app/formatter/table/image.go b/pkg/app/formatter/table/image.go index ff15a2b2c..e5cfc0568 100644 --- a/pkg/app/formatter/table/image.go +++ b/pkg/app/formatter/table/image.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,29 +18,17 @@ import ( "io" "strconv" + "github.com/cockroachdb/errors" "github.com/docker/docker/pkg/stringid" "github.com/docker/go-units" - "github.com/olekukonko/tablewriter" "github.com/tensorchord/envd/pkg/app/formatter" "github.com/tensorchord/envd/pkg/types" ) -func RenderImages(w io.Writer, imgs []types.EnvdImage) { - table := tablewriter.NewWriter(w) - table.SetHeader([]string{"Name", "Context", "GPU", "CUDA", "CUDNN", "Image ID", "Created", "Size"}) - - table.SetAutoWrapText(false) - table.SetAutoFormatHeaders(true) - table.SetHeaderAlignment(tablewriter.ALIGN_LEFT) - table.SetAlignment(tablewriter.ALIGN_LEFT) - table.SetCenterSeparator("") - table.SetColumnSeparator("") - table.SetRowSeparator("") - table.SetHeaderLine(false) - table.SetBorder(false) - table.SetTablePadding("\t") // pad with tabs - table.SetNoWhiteSpace(true) +func RenderImages(w io.Writer, imgs []types.EnvdImage) error { + table := CreateTable(w) + table.Header([]string{"Name", "Context", "GPU", "CUDA", "CUDNN", "Image ID", "Created", "Size"}) for _, img := range imgs { envRow := make([]string, 8) @@ -52,7 +40,10 @@ func RenderImages(w io.Writer, imgs []types.EnvdImage) { envRow[5] = stringid.TruncateID(img.Digest) envRow[6] = formatter.CreatedSinceString(img.Created) envRow[7] = units.HumanSizeWithPrecision(float64(img.Size), 3) - table.Append(envRow) + err := table.Append(envRow) + if err != nil { + return errors.Wrapf(err, "failed to append row for image %s", img.Name) + } } - table.Render() + return errors.Wrap(table.Render(), "failed to render image table") } diff --git a/pkg/app/formatter/table/version.go b/pkg/app/formatter/table/version.go index 32a7c4bad..80197a60f 100644 --- a/pkg/app/formatter/table/version.go +++ b/pkg/app/formatter/table/version.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/app/image.go b/pkg/app/image.go index e94a5e722..67f5c9f41 100644 --- a/pkg/app/image.go +++ b/pkg/app/image.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -70,7 +70,7 @@ func getImage(clicontext *cli.Context) error { format := clicontext.String("format") switch format { case "table": - table.RenderImages(os.Stdout, imgs) + return table.RenderImages(os.Stdout, imgs) case "json": return json.PrintImages(imgs) } diff --git a/pkg/app/image_describe.go b/pkg/app/image_describe.go index 558a0da22..3aef40e5b 100644 --- a/pkg/app/image_describe.go +++ b/pkg/app/image_describe.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -66,7 +66,7 @@ func getImageDependency(clicontext *cli.Context) error { format := clicontext.String("format") switch format { case "table": - table.RenderDependencies(os.Stdout, dep) + return table.RenderDependencies(os.Stdout, dep) case "json": return json.PrintEnvironmentDescriptions(dep, []types.PortBinding{}) } diff --git a/pkg/app/image_prune.go b/pkg/app/image_prune.go index 382093db7..320cbe917 100644 --- a/pkg/app/image_prune.go +++ b/pkg/app/image_prune.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -19,11 +19,12 @@ import ( "io" "os" - dockertypes "github.com/docker/docker/api/types" + "github.com/cockroachdb/errors" + dockerimage "github.com/docker/docker/api/types/image" "github.com/docker/go-units" - "github.com/olekukonko/tablewriter" "github.com/urfave/cli/v2" + "github.com/tensorchord/envd/pkg/app/formatter/table" "github.com/tensorchord/envd/pkg/app/telemetry" "github.com/tensorchord/envd/pkg/driver/docker" ) @@ -46,27 +47,15 @@ func pruneImages(clicontext *cli.Context) error { return err } if len(report.ImagesDeleted) > 0 { - renderPruneReport(os.Stdout, report) + return renderPruneReport(os.Stdout, report) } return nil } -func renderPruneReport(w io.Writer, report dockertypes.ImagesPruneReport) { - table := tablewriter.NewWriter(w) - table.SetHeader([]string{"Type", "Image"}) - - table.SetAutoWrapText(false) - table.SetAutoFormatHeaders(true) - table.SetHeaderAlignment(tablewriter.ALIGN_LEFT) - table.SetAlignment(tablewriter.ALIGN_LEFT) - table.SetCenterSeparator("") - table.SetColumnSeparator("") - table.SetRowSeparator("") - table.SetHeaderLine(false) - table.SetBorder(false) - table.SetTablePadding("\t") // pad with tabs - table.SetNoWhiteSpace(true) +func renderPruneReport(w io.Writer, report dockerimage.PruneReport) error { + table := table.CreateTable(w) + table.Header([]string{"Type", "Image"}) for _, img := range report.ImagesDeleted { envRow := make([]string, 2) @@ -77,8 +66,11 @@ func renderPruneReport(w io.Writer, report dockertypes.ImagesPruneReport) { envRow[0] = "Deleted" envRow[1] = img.Deleted } - table.Append(envRow) + err := table.Append(envRow) + if err != nil { + return errors.Wrapf(err, "failed to append row for image %s", img.Untagged) + } } - table.Render() fmt.Fprintln(w, "Total reclaimed space:", units.HumanSize(float64(report.SpaceReclaimed))) + return errors.Wrap(table.Render(), "failed to render prune report table") } diff --git a/pkg/app/image_prune_test.go b/pkg/app/image_prune_test.go index 63c9d0b67..d7499357b 100644 --- a/pkg/app/image_prune_test.go +++ b/pkg/app/image_prune_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,13 +18,13 @@ import ( "os" "testing" - "github.com/docker/docker/api/types" + dockerimage "github.com/docker/docker/api/types/image" ) func Test_renderPruneReport(t *testing.T) { - report := types.ImagesPruneReport{ - ImagesDeleted: []types.ImageDeleteResponseItem{ + report := dockerimage.PruneReport{ + ImagesDeleted: []dockerimage.DeleteResponse{ { Deleted: "sha256:123", }, @@ -34,5 +34,8 @@ func Test_renderPruneReport(t *testing.T) { }, SpaceReclaimed: 666666, } - renderPruneReport(os.Stdout, report) + err := renderPruneReport(os.Stdout, report) + if err != nil { + t.Errorf("renderPruneReport() error = %v", err) + } } diff --git a/pkg/app/image_remove.go b/pkg/app/image_remove.go index cf9412579..897b6931b 100644 --- a/pkg/app/image_remove.go +++ b/pkg/app/image_remove.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -46,12 +46,19 @@ var CommandRemoveImage = &cli.Command{ func removeImage(clicontext *cli.Context) error { imageName := clicontext.String("image") + tag := clicontext.String("tag") + + logger := logrus.WithFields(logrus.Fields{ + "cmd": "images remove", + "imageName": imageName, + "tag": tag, + }) + if imageName == "" { return errors.New("image name is required, find images by `envd images list`") } - tag := clicontext.String("tag") if tag == "" { - logrus.Debug("tag not specified, using default tag: `dev`") + logger.Debug("tag not specified, using default tag: `dev`") tag = "dev" } imageNameWithTag := fmt.Sprintf("%s:%s", imageName, tag) @@ -63,6 +70,6 @@ func removeImage(clicontext *cli.Context) error { if err := dockerClient.RemoveImage(clicontext.Context, imageNameWithTag); err != nil { return errors.Errorf("remove image %s failed: %w", imageNameWithTag, err) } - logrus.Infof("image(%s) has removed", imageNameWithTag) + logger.Info("image has been removed") return nil } diff --git a/pkg/app/init.go b/pkg/app/init.go index 2c843b814..8839d7922 100644 --- a/pkg/app/init.go +++ b/pkg/app/init.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -116,13 +116,22 @@ func isCondaEnvFile(file string) bool { } func initCommand(clicontext *cli.Context) error { - lang := strings.ToLower(clicontext.String("lang")) buildContext, err := filepath.Abs(clicontext.Path("path")) force := clicontext.Bool("force") if err != nil { return err } + filePath := filepath.Join(buildContext, "build.envd") + exists, err := fileutil.FileExists(filePath) + if err != nil { + return err + } + if exists && !force { + return errors.Errorf("build.envd already exists, use --force to overwrite it.\nOr you can run the command `envd up` to set up a new environment.") + } + + lang := strings.ToLower(clicontext.String("lang")) if !isValidLang(lang) { startQuestion(LanguageChoice) if len(selectionMap[LabelLanguage]) > 0 { @@ -139,21 +148,12 @@ func initCommand(clicontext *cli.Context) error { "init", telemetry.AddField("duration", time.Since(start).Seconds())) }(time.Now()) - filePath := filepath.Join(buildContext, "build.envd") - exists, err := fileutil.FileExists(filePath) - if err != nil { - return err - } - if exists && !force { - return errors.Errorf("build.envd already exists, use --force to overwrite it") - } - - if lang == "python" { - err = InitPythonEnv(buildContext) - if err != nil { + switch lang { + case "python": + if err = InitPythonEnv(buildContext); err != nil { return err } - } else if lang == "r" { + case "r": startQuestion(RPackageChoice) } diff --git a/pkg/app/interactive.go b/pkg/app/interactive.go index 53e0f7601..93a5e27cd 100644 --- a/pkg/app/interactive.go +++ b/pkg/app/interactive.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -67,11 +67,11 @@ type input struct { var LanguageChoice = input{ prompt: "Choose a programming language", - inputType: SINGLE_SELECT, + inputType: MULTIPLE_SELECT, label: LabelLanguage, options: []string{ "python", - "r", + "r_lang", "julia", }, } @@ -81,8 +81,10 @@ var PythonPackageChoice = input{ inputType: MULTIPLE_SELECT, label: LabelPythonPackage, options: []string{ + "ipython", "numpy", - "tensorflow", + "pandas", + "torch", }, } @@ -111,9 +113,9 @@ var CudaVersionChoice = input{ inputType: SINGLE_SELECT, label: LabelCuda, options: []string{ - "11.6.2", - "11.3.1", "11.2.2", + "11.8.0", + "12.3.2", }, } @@ -145,7 +147,6 @@ func (m model) View() string { case INPUT: // TODO: implement input if needed - } s += "\nPress q to quit. " @@ -225,7 +226,15 @@ func startQuestion(input input) { func generateFile(clicontext *cli.Context) error { var buf bytes.Buffer buf.WriteString("def build():\n") - buf.WriteString(fmt.Sprintf("%sbase(os=\"ubuntu20.04\", language=\"%s\")\n", indentation, selectionMap[LabelLanguage][0])) + buf.WriteString(fmt.Sprintf("%sbase(image=\"ubuntu:22.04\", dev=True)\n", indentation)) + for _, lang := range selectionMap[LabelLanguage] { + if lang == "python" { + buf.WriteString(fmt.Sprintf("%sinstall.conda()\n", indentation)) + buf.WriteString(fmt.Sprintf("%sinstall.python()\n", indentation)) + } else { + buf.WriteString(fmt.Sprintf("%sinstall.%s()\n", indentation, lang)) + } + } buf.WriteString(generatePackagesStr("python", selectionMap[LabelPythonPackage])) buf.WriteString(generatePackagesStr("r", selectionMap[LabelRPackage])) if len(selectionMap[LabelPythonRequirement]) > 0 { @@ -259,11 +268,8 @@ func generatePackagesStr(name string, packages []string) string { return "" } s := fmt.Sprintf("%sinstall.%s_packages(name = [\n", indentation, name) - for i, p := range packages { - s += fmt.Sprintf("%s\"%s\"", strings.Repeat(indentation, 2), p) - if i != len(packages)-1 { - s += ", " - } + for _, p := range packages { + s += fmt.Sprintf("%s\"%s\",", strings.Repeat(indentation, 2), p) s += "\n" } s += fmt.Sprintf("%s])\n", indentation) diff --git a/pkg/app/login.go b/pkg/app/login.go index 831c81d96..8b35b973a 100644 --- a/pkg/app/login.go +++ b/pkg/app/login.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -60,8 +60,11 @@ func login(clicontext *cli.Context) error { if err != nil { return errors.Wrap(err, "failed to get current context") } + + logger := logrus.WithField("cmd", "login") + if c.Runner == types.RunnerTypeDocker { - logrus.Warn("login is not needed for docker runner, skipping") + logger.Warn("login is not needed for docker runner, skipping") return nil } hostAddr := c.RunnerAddress @@ -84,13 +87,13 @@ func login(clicontext *cli.Context) error { auth := true if pwd == "" { auth = false - logrus.Warn("The password is nil, skip the authentication. Please make sure that the server is running in no-auth mode") + logger.Warn("The password is nil, skip the authentication. Please make sure that the server is running in no-auth mode") if loginName == "" { loginName, err = generateLoginName() if err != nil { return errors.Wrap(err, "failed to generate the login name") } - logrus.Warnf("The login name is nil, use `%s` as the login name", loginName) + logger.Warnf("The login name is nil, use `%s` as the login name", loginName) } } req := servertypes.AuthNRequest{ @@ -98,7 +101,7 @@ func login(clicontext *cli.Context) error { Password: pwd, } - logger := logrus.WithFields(logrus.Fields{ + logger = logger.WithFields(logrus.Fields{ "login_name": loginName, "auth-enabled": auth, }) @@ -131,6 +134,7 @@ func login(clicontext *cli.Context) error { } } + logger.Debug("login request after register") resp, err = cli.Login(clicontext.Context, req) if err != nil { return errors.Wrap(err, "failed to get the response from envd-server client") @@ -159,7 +163,7 @@ func login(clicontext *cli.Context) error { } } - logrus.WithField("key", keyResp.Name).Debug("key is added successfully") + logger.WithField("key", keyResp.Name).Debug("key is added successfully") if err := home.GetManager().AuthCreate(types.AuthConfig{ Name: resp.LoginName, JWTToken: resp.IdentityToken, diff --git a/pkg/app/new.go b/pkg/app/new.go new file mode 100644 index 000000000..0239b3f42 --- /dev/null +++ b/pkg/app/new.go @@ -0,0 +1,170 @@ +// Copyright 2025 The envd 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 app + +import ( + "embed" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/cockroachdb/errors" + "github.com/sirupsen/logrus" + cli "github.com/urfave/cli/v2" + + "github.com/tensorchord/envd/pkg/util/fileutil" +) + +var ( + //go:embed template/*.envd + envdTemplateDir embed.FS + defaultEnvdTemplates []EnvdTemplate +) + +type EnvdTemplate struct { + name string + content []byte +} + +func init() { + entries, err := envdTemplateDir.ReadDir("template") + if err != nil { + panic(errors.Wrap(err, "failed to read template directory")) + } + for _, entry := range entries { + if entry.IsDir() { + continue + } + name := strings.TrimSuffix(entry.Name(), ".envd") + content, err := envdTemplateDir.ReadFile(filepath.Join("template", entry.Name())) + if err != nil { + panic(errors.Wrapf(err, "failed to read template file: %s", entry.Name())) + } + defaultEnvdTemplates = append(defaultEnvdTemplates, EnvdTemplate{name: name, content: content}) + } +} + +func isDefaultTemplate(name string) bool { + for _, template := range defaultEnvdTemplates { + if template.name == name { + return true + } + } + return false +} + +var CommandNew = &cli.Command{ + Name: "new", + Category: CategoryBasic, + Aliases: []string{"n"}, + Usage: "Create a new `build.envd` file from pre-defined templates", + Description: `The template used by this command is stored in the + '$HOME/.config/envd/templates' directory, we provide some pre-defined templates for you + to use during 'envd bootstrap', you can also add your own templates to this directory.`, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "template", + Usage: "Template name to use (`envd bootstrap` will add some default templates to '$HOME/.config/envd/templates')", + Aliases: []string{"t"}, + Required: true, + }, + &cli.BoolFlag{ + Name: "force", + Usage: "Overwrite the build.envd if existed", + Aliases: []string{"f"}, + Required: false, + }, + &cli.PathFlag{ + Name: "path", + Usage: "Path to the directory of the build.envd", + Aliases: []string{"p"}, + Value: ".", + }, + }, + Action: newCommand, +} + +func newCommand(clicontext *cli.Context) error { + workDir, err := filepath.Abs(clicontext.Path("path")) + if err != nil { + return errors.Wrap(err, "failed to get absolute path") + } + + force := clicontext.Bool("force") + filePath := filepath.Join(workDir, "build.envd") + exists, err := fileutil.FileExists(filePath) + if err != nil { + return errors.Wrap(err, "failed to check file exists") + } + if exists && !force { + return errors.New("build.envd already exists, use `--force` to overwrite") + } + + template := clicontext.String("template") + templateFile := fmt.Sprintf("%s.envd", template) + templatePath, err := fileutil.TemplateFile(templateFile) + if err != nil { + return errors.Wrapf(err, "failed to get template file: `%s`", templateFile) + } + + content, err := os.ReadFile(templatePath) + if err != nil { + if os.IsNotExist(err) && isDefaultTemplate(template) { + // Add default templates to the template directory if not exist + err = addTemplates(clicontext) + if err != nil { + return err + } + content, err = os.ReadFile(templatePath) + if err != nil { + return err + } + } else { + return errors.Wrapf(err, "failed to read the template file `%s`", templatePath) + } + } + err = os.WriteFile(filePath, content, 0644) + if err != nil { + return errors.Wrapf(err, "failed to write the build.envd file") + } + logrus.Infof("Template `%s` is created in `%s`", template, filePath) + + return nil +} + +func addTemplates(clicontext *cli.Context) error { + for _, template := range defaultEnvdTemplates { + file, err := fileutil.TemplateFile(template.name + ".envd") + if err != nil { + return errors.Wrapf(err, "failed to get template file path: %s", template.name) + } + exist, err := fileutil.FileExists(file) + if err != nil { + return errors.Wrapf(err, "failed to check file exists: %s", file) + } + if exist { + logrus.Debugf("Template file `%s` already exists in `%s`", template.name, file) + continue + } + err = os.WriteFile(file, template.content, 0644) + if err != nil { + return errors.Wrapf(err, "failed to write template file: %s", template.name) + } + logrus.Debugf("Template file `%s` is added to `%s`", template.name, file) + } + + return nil +} diff --git a/pkg/app/pause.go b/pkg/app/pause.go index 50c91f825..4fd7c8ec3 100644 --- a/pkg/app/pause.go +++ b/pkg/app/pause.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -60,7 +60,7 @@ func pause(clicontext *cli.Context) error { return errors.Wrap(err, "failed to pause the environment") } if name != "" { - logrus.Infof("%s is paused", name) + logrus.WithField("cmd", "pause").Infof("%s is paused", name) } return nil } diff --git a/pkg/app/prune.go b/pkg/app/prune.go index 555fe35a8..ef820165e 100644 --- a/pkg/app/prune.go +++ b/pkg/app/prune.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import ( "github.com/tensorchord/envd/pkg/app/telemetry" "github.com/tensorchord/envd/pkg/buildkitd" "github.com/tensorchord/envd/pkg/home" + "github.com/tensorchord/envd/pkg/types" ) var CommandPrune = &cli.Command{ @@ -69,7 +70,9 @@ func prune(clicontext *cli.Context) error { }(time.Now()) keepDuration := clicontext.Duration("keep-duration") - keepStorage := clicontext.Float64("keep-storage") + maxUsed := clicontext.Float64("max-used-mb") + minFree := clicontext.Float64("min-free-mb") + reserved := clicontext.Float64("reserved-mb") filter := clicontext.StringSlice("filter") verbose := clicontext.Bool("verbose") @@ -77,13 +80,22 @@ func prune(clicontext *cli.Context) error { if err != nil { return errors.Wrap(err, "failed to get the current context") } - bkClient, err := buildkitd.NewClient(clicontext.Context, - c.Builder, c.BuilderAddress, "") - if err != nil { - return errors.Wrap(err, "failed to create buildkit client") + var bkClient buildkitd.Client + if c.Builder == types.BuilderTypeMoby { + bkClient, err = buildkitd.NewMobyClient(clicontext.Context, + c.Builder, c.BuilderAddress, nil) + if err != nil { + return errors.Wrap(err, "failed to create moby buildkit client") + } + } else { + bkClient, err = buildkitd.NewClient(clicontext.Context, + c.Builder, c.BuilderAddress, nil) + if err != nil { + return errors.Wrap(err, "failed to create buildkit client") + } } if err := bkClient.Prune(clicontext.Context, - keepDuration, keepStorage, filter, verbose, cleanAll); err != nil { + keepDuration, maxUsed, minFree, reserved, filter, verbose, cleanAll); err != nil { return errors.Wrap(err, "failed to prune buildkit cache") } return nil diff --git a/pkg/app/reference.go b/pkg/app/reference.go new file mode 100644 index 000000000..eceed3cca --- /dev/null +++ b/pkg/app/reference.go @@ -0,0 +1,62 @@ +// Copyright 2023 The envd 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 app + +import ( + "fmt" + "os" + + "github.com/cockroachdb/errors" + "github.com/urfave/cli/v2" +) + +var CommandReference = &cli.Command{ + Name: "reference", + Category: CategoryOther, + Usage: "Print envd reference documentation", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "output", + Usage: "Output file, if not specified, print to stderr", + Value: "", + }, + }, + Action: outputReference, +} + +const referenceHeader = ` +# envd CLI Reference + +This is a reference for the CLI commands of envd. + +::: tip +The documentation is auto-generated from [envd app](https://github.com/tensorchord/envd/blob/main/pkg/app/app.go), please do not edit it manually. +::: + +` + +func outputReference(clicontext *cli.Context) error { + doc, err := clicontext.App.ToMarkdown() + if err != nil { + return errors.Wrap(err, "failed to generate the markdown document") + } + content := referenceHeader + doc + output := clicontext.String("output") + if len(output) > 0 { + return os.WriteFile(output, []byte(content), 0644) + } + fmt.Println(content) + return nil +} diff --git a/pkg/app/resume.go b/pkg/app/resume.go index 3f819a4c4..e6bde139a 100644 --- a/pkg/app/resume.go +++ b/pkg/app/resume.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -60,7 +60,7 @@ func resume(clicontext *cli.Context) error { return errors.Wrap(err, "failed to pause the environment") } if name != "" { - logrus.Infof("%s is resumed", name) + logrus.WithField("cmd", "resume").Infof("%s is resumed", name) } return nil } diff --git a/pkg/app/run.go b/pkg/app/run.go index 09705741c..1192bfe2d 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package app import ( "fmt" + "path/filepath" "strings" "time" @@ -30,7 +31,9 @@ import ( "github.com/tensorchord/envd/pkg/home" "github.com/tensorchord/envd/pkg/ssh" sshconfig "github.com/tensorchord/envd/pkg/ssh/config" + "github.com/tensorchord/envd/pkg/syncthing" "github.com/tensorchord/envd/pkg/types" + "github.com/tensorchord/envd/pkg/util/fileutil" "github.com/tensorchord/envd/pkg/util/netutil" ) @@ -69,9 +72,15 @@ var CommandCreate = &cli.Command{ Value: sshconfig.GetPrivateKeyOrPanic(), Hidden: true, }, + &cli.PathFlag{ + Name: "path", + Usage: "Working directory path to be used as project root", + Aliases: []string{"p"}, + Value: ".", + }, &cli.StringFlag{ Name: "host", - Usage: "Assign the host address for environment ssh acesss server listening", + Usage: "Assign the host address for the environment SSH access server listening", Value: envd.Localhost, }, &cli.IntFlag{ @@ -80,7 +89,7 @@ var CommandCreate = &cli.Command{ Value: 2048, }, &cli.StringFlag{ - Name: "cpu", + Name: "cpus", Usage: "Request CPU resources (number of cores), such as 0.5, 1, 2", Value: "", }, @@ -94,6 +103,16 @@ var CommandCreate = &cli.Command{ Usage: "Request GPU resources (number of gpus), such as 1, 2", Value: "", }, + &cli.BoolFlag{ + Name: "sync", + Usage: "Sync the local directory with the remote container", + Value: false, + }, + &cli.StringSliceFlag{ + Name: "volume", + Usage: "Mount host directory into container", + Aliases: []string{"v"}, + }, }, Action: run, } @@ -103,9 +122,6 @@ func run(clicontext *cli.Context) error { if err != nil { return errors.Wrap(err, "failed to get current context") } - if c.Runner == types.RunnerTypeDocker { - return errors.Newf("docker runner is not supported for this command, please use `envd up`") - } telemetry.GetReporter().Telemetry( "run", telemetry.AddField("runner", c.Runner)) @@ -125,22 +141,45 @@ func run(clicontext *cli.Context) error { Image: clicontext.String("image"), Timeout: clicontext.Duration("timeout"), NumMem: clicontext.String("memory"), - NumCPU: clicontext.String("cpu"), + NumCPU: clicontext.String("cpus"), NumGPU: clicontext.Int("gpu"), ShmSize: clicontext.Int("shm-size"), EnvironmentName: name, } - if c.Runner == types.RunnerTypeEnvdServer { - opt.EnvdServerSource = &envd.EnvdServerSource{} + switch c.Runner { + case types.RunnerTypeEnvdServer: + opt.EnvdServerSource = &envd.EnvdServerSource{ + Sync: clicontext.Bool("sync"), + } + if len(clicontext.StringSlice("volume")) > 0 { + return errors.New("volume is not supported for envd-server runner") + } + case types.RunnerTypeDocker: + opt.DockerSource = &envd.DockerSource{ + MountOptions: clicontext.StringSlice("volume"), + } + + buildContext, err := filepath.Abs(clicontext.Path("path")) + if err != nil { + return errors.Wrap(err, "failed to get absolute path of the build context") + } + opt.BuildContext = buildContext } + res, err := engine.StartEnvd(clicontext.Context, opt) if err != nil { return err } - logrus.Debugf("container %s is running", res.Name) + logger := logrus.WithFields(logrus.Fields{ + "cmd": "run", + "StartOptions": opt, + "StartResult": res, + }) + + logger.Debugf("container %s is running", res.Name) - logrus.Debugf("add entry %s to SSH config.", res.Name) + logger.Debugf("add entry %s to SSH config.", res.Name) hostname, err := c.GetSSHHostname(opt.SshdHost) if err != nil { return errors.Wrap(err, "failed to get the ssh hostname") @@ -154,6 +193,7 @@ func run(clicontext *cli.Context) error { if err != nil { return errors.Wrap(err, "failed to get the username") } + eo := sshconfig.EntryOptions{ Name: res.Name, IFace: hostname, @@ -164,7 +204,8 @@ func run(clicontext *cli.Context) error { User: username, } if err = sshconfig.AddEntry(eo); err != nil { - logrus.Infof("failed to add entry %s to your SSH config file: %s", res.Name, err) + logger.WithError(err). + Infof("failed to add entry %s to your SSH config file", res.Name) return errors.Wrap(err, "failed to add entry to your SSH config file") } @@ -197,7 +238,7 @@ func run(clicontext *cli.Context) error { } localAddress := fmt.Sprintf("%s:%d", "localhost", localPort) remoteAddress := fmt.Sprintf("%s:%d", "localhost", p.Port) - logrus.Infof("service \"%s\" is listening at %s\n", p.Name, localAddress) + logger.Infof(`service "%s" is listening at %s\n`, p.Name, localAddress) go func() { if err := sshClient.LocalForward(localAddress, remoteAddress); err != nil { outputChannel <- errors.Wrap(err, "failed to forward to local port") @@ -205,6 +246,35 @@ func run(clicontext *cli.Context) error { }() } + if clicontext.Bool("sync") { + go func() { + if err := sshClient.LocalForward(syncthing.DefaultRemoteAPIAddress, syncthing.DefaultRemoteAPIAddress); err != nil { + outputChannel <- errors.Wrap(err, "failed to forward to remote api port") + } + }() + + go func() { + syncthingRemoteAddr := fmt.Sprintf("127.0.0.1:%s", syncthing.ParsePortFromAddress(syncthing.DefaultRemoteDeviceAddress)) + if err := sshClient.LocalForward(syncthingRemoteAddr, syncthingRemoteAddr); err != nil { + outputChannel <- errors.Wrap(err, "failed to forward to remote port") + } + }() + + go func() { + syncthingLocalAddr := fmt.Sprintf("127.0.0.1:%s", syncthing.ParsePortFromAddress(syncthing.DefaultLocalDeviceAddress)) + if err := sshClient.RemoteForward(syncthingLocalAddr, syncthingLocalAddr); err != nil { + outputChannel <- errors.Wrap(err, "failed to forward to local port") + } + }() + + localSyncthing, _, err := startSyncthing(res.Name) + if err != nil { + return errors.Wrap(err, "failed to start syncthing") + } + defer localSyncthing.StopLocalSyncthing() + + } + go func() { // TODO(gaocegege): Avoid the hard code. if err := sshClient.Attach(); err != nil { @@ -219,3 +289,40 @@ func run(clicontext *cli.Context) error { } return nil } + +func startSyncthing(name string) (*syncthing.Syncthing, *syncthing.Syncthing, error) { + cwd, err := fileutil.CWD() + if err != nil { + return nil, nil, errors.Wrap(err, "failed to get current working directory") + } + projectName := filepath.Base(cwd) + + logger := logrus.WithFields(logrus.Fields{ + "cwd": cwd, + "projectName": projectName, + }) + + localSyncthing, err := syncthing.InitializeLocalSyncthing(name) + if err != nil { + return nil, nil, errors.Wrap(err, "failed to initialize local syncthing") + } + + remoteSyncthing, err := syncthing.InitializeRemoteSyncthing() + if err != nil { + return nil, nil, errors.Wrap(err, "failed to initialize remote syncthing") + } + logger.Debug("Remote syncthing initialized") + + err = syncthing.ConnectDevices(localSyncthing, remoteSyncthing) + if err != nil { + return nil, nil, errors.Wrap(err, "failed to connect devices") + } + logger.Debug("Syncthing devices connected") + + err = syncthing.SyncFolder(localSyncthing, remoteSyncthing, cwd, fmt.Sprintf("%s/%s", fileutil.EnvdHomeDir(), projectName)) + if err != nil { + return nil, nil, errors.Wrap(err, "failed to sync folders") + } + + return localSyncthing, remoteSyncthing, nil +} diff --git a/pkg/app/telemetry/reporter.go b/pkg/app/telemetry/reporter.go index daa0ee4c7..0ed7cf81a 100644 --- a/pkg/app/telemetry/reporter.go +++ b/pkg/app/telemetry/reporter.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -130,9 +130,10 @@ func (r *defaultReporter) dumpTelemetry() error { } func (r *defaultReporter) Identify() { - logrus.WithField("UID", r.UID).Debug("telemetry initialization") + logger := logrus.WithField("UID", r.UID) + logger.Debug("telemetry initialization") if r.enabled { - logrus.Debug("sending telemetry") + logger.Debug("sending telemetry") v := version.GetVersion() if err := r.client.Enqueue(segmentio.Identify{ UserId: r.UID, @@ -149,7 +150,7 @@ func (r *defaultReporter) Identify() { Timestamp: time.Now(), Traits: segmentio.NewTraits(), }); err != nil { - logrus.Warn("telemetry failed") + logger.Debug("telemetry failed") return } } @@ -163,10 +164,11 @@ func AddField(name string, value interface{}) TelemetryField { func (r *defaultReporter) Telemetry(command string, fields ...TelemetryField) { if r.enabled { - logrus.WithFields(logrus.Fields{ + logger := logrus.WithFields(logrus.Fields{ "UID": r.UID, "command": command, - }).Debug("sending telemetry track event") + }) + logger.Debug("sending telemetry track event") t := segmentio.Track{ UserId: r.UID, Event: command, @@ -176,7 +178,7 @@ func (r *defaultReporter) Telemetry(command string, fields ...TelemetryField) { field(&t.Properties) } if err := r.client.Enqueue(t); err != nil { - logrus.Warn(err) + logger.Debug(err) } // make sure the msg can be sent out r.client.Close() diff --git a/e2e/v1/cli/testdata/build-test/build.envd b/pkg/app/template/conda.envd similarity index 80% rename from e2e/v1/cli/testdata/build-test/build.envd rename to pkg/app/template/conda.envd index d1f3175fa..5e9741363 100644 --- a/e2e/v1/cli/testdata/build-test/build.envd +++ b/pkg/app/template/conda.envd @@ -1,7 +1,5 @@ -# syntax=v1 - - def build(): base(dev=True) install.conda() install.python() + shell("fish") diff --git a/pkg/app/template/julia.envd b/pkg/app/template/julia.envd deleted file mode 100644 index 12f347ece..000000000 --- a/pkg/app/template/julia.envd +++ /dev/null @@ -1,11 +0,0 @@ -def build(): - # Use ubuntu20.04 as base image and install julia - base(os="ubuntu20.04", language="julia") - # Uncomment line below to enable Pypi mirror - # config.julia_pkg_server(url="https://mirrors.tuna.tsinghua.edu.cn/julia") - - # Add the packages you are using here - install.julia_packages(["Example"]) - - # Select the shell environment you like - shell("zsh") diff --git a/pkg/app/template/pixi.envd b/pkg/app/template/pixi.envd new file mode 100644 index 000000000..0b0f2181e --- /dev/null +++ b/pkg/app/template/pixi.envd @@ -0,0 +1,4 @@ +def build(): + base(dev=True) + install.pixi() + shell("fish") diff --git a/pkg/app/template/r.envd b/pkg/app/template/r.envd deleted file mode 100644 index bbf60a229..000000000 --- a/pkg/app/template/r.envd +++ /dev/null @@ -1,14 +0,0 @@ -def build(): - # Use ubuntu20.04 as base image and install r - base(os="ubuntu20.04", language="r") - - # Add the packages you are using here - install.r_packages( - [ - "remotes", - "rlang", - ] - ) - - # Select the shell environment you like - shell("zsh") diff --git a/pkg/app/template/torch.envd b/pkg/app/template/torch.envd new file mode 100644 index 000000000..6010ff70f --- /dev/null +++ b/pkg/app/template/torch.envd @@ -0,0 +1,35 @@ +def default(): + shell("fish") + install.conda() + install.python() + install.apt_packages(name=["build-essential"]) + + +def others(): + install.python_packages( + name=[ + "transformers", + ] + ) + + +def gpu(): + base(dev=True, image="nvidia/cuda:12.6.3-cudnn-devel-ubuntu22.04") + default() + install.python_packages( + name=["torch --index-url https://download.pytorch.org/whl/cu126"] + ) + others() + + +def cpu(): + base(dev=True) + default() + install.python_packages( + name=["torch --index-url https://download.pytorch.org/whl/cpu"] + ) + others() + + +def build(): + cpu() diff --git a/pkg/app/template/uv.envd b/pkg/app/template/uv.envd new file mode 100644 index 000000000..f1956111e --- /dev/null +++ b/pkg/app/template/uv.envd @@ -0,0 +1,4 @@ +def build(): + base(dev=True) + install.uv() + shell("fish") diff --git a/pkg/app/top.go b/pkg/app/top.go index 78ff9f382..3768e18fb 100644 --- a/pkg/app/top.go +++ b/pkg/app/top.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/app/up.go b/pkg/app/up.go index b2078a76b..45ab926ee 100644 --- a/pkg/app/up.go +++ b/pkg/app/up.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package app import ( "fmt" "path/filepath" + "strconv" "time" "github.com/cockroachdb/errors" @@ -29,6 +30,7 @@ import ( "github.com/tensorchord/envd/pkg/home" sshconfig "github.com/tensorchord/envd/pkg/ssh/config" "github.com/tensorchord/envd/pkg/types" + "github.com/tensorchord/envd/pkg/util/runtimeutil" ) var CommandUp = &cli.Command{ @@ -43,6 +45,11 @@ var CommandUp = &cli.Command{ Aliases: []string{"t"}, DefaultText: "PROJECT:dev", }, + &cli.StringFlag{ + Name: "name", + Usage: "environment name", + Value: "", + }, &cli.PathFlag{ Name: "path", Usage: "Path to the directory containing the build.envd", @@ -90,6 +97,21 @@ var CommandUp = &cli.Command{ Usage: "Configure the shared memory size (megabyte)", Value: 2048, }, + &cli.StringFlag{ + Name: "cpus", + Usage: "Request CPU resources (number of cores), such as 0.5, 1, 2", + Value: "", + }, + &cli.StringFlag{ + Name: "cpu-set", + Usage: "Limit the specific CPUs or cores the environment can use, such as `0-3`, `1,3`", + Value: "", + }, + &cli.StringFlag{ + Name: "memory", + Usage: "Request Memory, such as 512Mb, 2Gb", + Value: "", + }, &cli.BoolFlag{ Name: "detach", Usage: "Detach from the container", @@ -97,9 +119,19 @@ var CommandUp = &cli.Command{ }, &cli.BoolFlag{ Name: "no-gpu", - Usage: "Launch the CPU container", + Usage: "Launch the CPU container even if it's a GPU image", Value: false, }, + &cli.IntFlag{ + Name: "gpus", + Usage: "Number of GPUs used in this environment, this will override the `config.gpu()`", + Value: 0, + }, + &cli.StringFlag{ + Name: "gpu-set", + Usage: "GPU devices used in this environment, such as `all`, `'\"device=1,3\"'`, `count=2`(all to pass all GPUs). This will override the `--gpus`", + Value: "", + }, &cli.BoolFlag{ Name: "force", Usage: "Force rebuild and run the container although the previous container is running", @@ -107,20 +139,25 @@ var CommandUp = &cli.Command{ }, &cli.StringFlag{ Name: "host", - Usage: "Assign the host address for environment ssh acesss server listening", + Usage: "Assign the host address for the environment SSH access server listening", Value: envd.Localhost, }, // https://github.com/urfave/cli/issues/1134#issuecomment-1191407527 &cli.StringFlag{ Name: "export-cache", - Usage: "Export the cache (e.g. type=registry,ref=<image>)", + Usage: "Export the cache (e.g. `type=registry,ref=<image>`). The default `moby-worker` builder doesn't support this unless the docker-ce has enabled the `containerd` image store. You can run `envd context create --name docker --builder docker-container --use` to use this feature.", Aliases: []string{"ec"}, }, &cli.StringFlag{ Name: "import-cache", - Usage: "Import the cache (e.g. type=registry,ref=<image>)", + Usage: "Import the cache (e.g. `type=registry,ref=<image>`)", Aliases: []string{"ic"}, }, + &cli.StringFlag{ + Name: "platform", + Usage: "Specify the target platform for the build output, (for example, windows/amd64, linux/amd64, or darwin/arm64)", + DefaultText: runtimeutil.GetRuntimePlatform(), + }, }, Action: up, @@ -151,6 +188,7 @@ func up(clicontext *cli.Context) error { ctr := filepath.Base(buildOpt.BuildContextDir) detach := clicontext.Bool("detach") logger := logrus.WithFields(logrus.Fields{ + "cmd": "up", "builder-options": buildOpt, "container-name": ctr, "detach": detach, @@ -164,6 +202,9 @@ func up(clicontext *cli.Context) error { if err = buildutil.InterpretEnvdDef(builder); err != nil { return err } + if !builder.GetGraph().IsDev() { + return errors.New("`envd up` only works for dev images. If you're using v1, please enable dev with `base(dev=True)`.") + } if err = buildutil.DetectEnvironment(clicontext, buildOpt); err != nil { return err } @@ -171,19 +212,41 @@ func up(clicontext *cli.Context) error { return err } - logrus.Debug("start running the environment") + logger.Debug("start running the environment") // Do not attach GPU if the flag is set. - gpuEnable := clicontext.Bool("no-gpu") - var gpu bool - if gpuEnable { - gpu = false + disableGPU := clicontext.Bool("no-gpu") + var defaultGPU bool + if disableGPU { + defaultGPU = false } else { - gpu = builder.GPUEnabled() + defaultGPU = builder.GPUEnabled() } numGPU := 0 - if gpu { + if defaultGPU { numGPU = 1 } + configGPU := builder.NumGPUs() + if defaultGPU && configGPU != 0 { + numGPU = configGPU + } + cliGPU := clicontext.Int("gpus") + if defaultGPU && cliGPU != 0 { + numGPU = cliGPU + } + gpuSet := "" + if defaultGPU && numGPU != 0 { + gpuSet = strconv.Itoa(numGPU) + } + cliGPUSet := clicontext.String("gpu-set") + if defaultGPU && len(cliGPUSet) > 0 { + gpuSet = cliGPUSet + } + + shmSize := builder.ShmSize() + isSetShmSize := clicontext.IsSet("shm-size") + if shmSize == 0 || isSetShmSize { + shmSize = clicontext.Int("shm-size") + } opt := envd.Options{ Context: c, @@ -192,20 +255,31 @@ func up(clicontext *cli.Context) error { if err != nil { return errors.Wrap(err, "failed to create the docker client") } - name, err := buildutil.CreateEnvNameFromDir(buildOpt.BuildContextDir) - if err != nil { - return errors.Wrapf(err, "failed to create the env name from %s", buildOpt.BuildContextDir) + name := clicontext.String("name") + if name == "" { + name, err = buildutil.CreateEnvNameFromDir(buildOpt.BuildContextDir) + if err != nil { + return errors.Wrapf(err, "failed to create the env name from %s", buildOpt.BuildContextDir) + } } startOptions := envd.StartOptions{ EnvironmentName: name, BuildContext: buildOpt.BuildContextDir, Image: buildOpt.Tag, NumGPU: numGPU, + GPUSet: gpuSet, Forced: clicontext.Bool("force"), Timeout: clicontext.Duration("timeout"), SshdHost: clicontext.String("host"), - ShmSize: clicontext.Int("shm-size"), + ShmSize: shmSize, + NumCPU: clicontext.String("cpus"), + NumMem: clicontext.String("memory"), + CPUSet: clicontext.String("cpu-set"), + } + if len(startOptions.NumCPU) > 0 && len(startOptions.CPUSet) > 0 { + return errors.New("`--cpus` and `--cpu-set` are mutually exclusive") } + if c.Runner != types.RunnerTypeEnvdServer { startOptions.EngineSource = envd.EngineSource{ DockerSource: &envd.DockerSource{ @@ -221,9 +295,9 @@ func up(clicontext *cli.Context) error { if err != nil { return errors.Wrap(err, "failed to start the envd environment") } - logrus.Debugf("container %s is running", res.Name) + logger.Debugf("container %s is running", res.Name) - logrus.Debugf("add entry %s to SSH config.", ctr) + logger.Debugf("add entry %s to SSH config.", ctr) hostname, err := c.GetSSHHostname(startOptions.SshdHost) if err != nil { return errors.Wrap(err, "failed to get the ssh hostname") @@ -235,7 +309,8 @@ func up(clicontext *cli.Context) error { return errors.Wrap(err, "failed to get the ssh entry") } if err = sshconfig.AddEntry(eo); err != nil { - logrus.Infof("failed to add entry %s to your SSH config file: %s", ctr, err) + logger.WithError(err). + Infof("failed to add entry %s to your SSH config file", ctr) return errors.Wrap(err, "failed to add entry to your SSH config file") } telemetry.GetReporter().Telemetry( diff --git a/pkg/app/version.go b/pkg/app/version.go index ad5c0ee1a..253eecd39 100644 --- a/pkg/app/version.go +++ b/pkg/app/version.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/autocomplete/bash.go b/pkg/autocomplete/bash.go index f970838c6..a913e094b 100644 --- a/pkg/autocomplete/bash.go +++ b/pkg/autocomplete/bash.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import ( "runtime" "github.com/cockroachdb/errors" + "github.com/urfave/cli/v2" "github.com/tensorchord/envd/pkg/util/fileutil" ) @@ -48,7 +49,7 @@ complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete $PROG unset PROG ` -func InsertBashCompleteEntry() error { +func InsertBashCompleteEntry(clicontext *cli.Context) error { var path string if runtime.GOOS == "darwin" { path = "/usr/local/etc/bash_completion.d/envd" @@ -65,33 +66,16 @@ func InsertBashCompleteEntry() error { return errors.Errorf("unable to enable bash-completion: %s does not exist", dirPath) } - pathExists, err := fileutil.FileExists(path) - if err != nil { - return errors.Wrapf(err, "failed checking if %s exists", path) - } - if pathExists { - return nil // file already exists, don't update it. - } - - // create the completion file - f, err := os.Create(path) - if err != nil { - return err - } - defer f.Close() - - bashEntry, err := BashCompleteEntry() + bashEntry, err := BashCompleteEntry(clicontext) if err != nil { return errors.Wrapf(err, "unable to enable bash-completion") } - - _, err = f.Write([]byte(bashEntry)) - if err != nil { + if err = os.WriteFile(path, []byte(bashEntry), 0644); err != nil { return errors.Wrapf(err, "failed writing to %s", path) } return nil } -func BashCompleteEntry() (string, error) { +func BashCompleteEntry(_ *cli.Context) (string, error) { return autocompleteBASH, nil } diff --git a/pkg/autocomplete/fish.go b/pkg/autocomplete/fish.go new file mode 100644 index 000000000..1212e539c --- /dev/null +++ b/pkg/autocomplete/fish.go @@ -0,0 +1,61 @@ +// Copyright 2023 The envd 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 autocomplete + +import ( + "os" + "os/exec" + "path/filepath" + + "github.com/cockroachdb/errors" + "github.com/urfave/cli/v2" + + "github.com/tensorchord/envd/pkg/util/fileutil" +) + +func InsertFishCompleteEntry(clicontext *cli.Context) error { + _, err := exec.LookPath("fish") + if err != nil { + return errors.Errorf("can't find fish in this system, stop settings the fish-completion") + } + + homeDir, err := os.UserHomeDir() + if err != nil { + return errors.Wrapf(err, "unable obtain user directory", err) + } + path := filepath.Join(homeDir, ".config/fish/completions/envd.fish") + dirPath := filepath.Dir(path) + + dirPathExists, err := fileutil.DirExists(dirPath) + if err != nil { + return errors.Wrapf(err, "failed checking if %s exists", dirPath) + } + if !dirPathExists { + return errors.Errorf("unable to enable fish-completion: %s does not exists", dirPath) + } + + fishEntry, err := FishCompleteEntry(clicontext) + if err != nil { + return errors.Wrapf(err, "unable to enable fish-completion") + } + if err = os.WriteFile(path, []byte(fishEntry), 0644); err != nil { + return errors.Wrapf(err, "failed writing to %s", path) + } + return nil +} + +func FishCompleteEntry(clicontext *cli.Context) (string, error) { + return clicontext.App.ToFishCompletion() +} diff --git a/pkg/autocomplete/zsh.go b/pkg/autocomplete/zsh.go index 5471e8137..fde52ab50 100644 --- a/pkg/autocomplete/zsh.go +++ b/pkg/autocomplete/zsh.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -23,7 +23,8 @@ import ( "strings" "github.com/cockroachdb/errors" - "github.com/containerd/containerd/log" + "github.com/containerd/log" + "github.com/urfave/cli/v2" "github.com/tensorchord/envd/pkg/util/fileutil" ) @@ -59,24 +60,25 @@ var zshConfig = ` ` // If debugging this, it might be required to run `rm ~/.zcompdump*` to remove the cache -func InsertZSHCompleteEntry() error { +func InsertZSHCompleteEntry(clicontext *cli.Context) error { // check the system has zsh _, err := exec.LookPath("zsh") if err != nil { return errors.Errorf("can't find zsh in this system, stop setting the zsh-completion.") } + homeDir, err := os.UserHomeDir() + if err != nil { + return errors.Wrapf(err, "unable obtain user directory", err) + } // should be the same on linux and macOS filename := "envd.zsh" - homeDir := os.Getenv("HOME") dirs := []string{ "/usr/share/zsh/site-functions", "/usr/local/share/zsh/site-functions", fileutil.DefaultConfigDir, } - var f *os.File - var lastErr error path := "" for _, dir := range dirs { dirPathExists, err := fileutil.DirExists(dir) @@ -86,42 +88,17 @@ func InsertZSHCompleteEntry() error { if dirPathExists { path = fmt.Sprintf("%s/%s", dir, filename) log.L.Debugf("use the zsh-completion path for envd: %s", path) - - pathExists, err := fileutil.FileExists(path) - if err != nil { - lastErr = errors.Wrapf(err, "failed to check if %s exists", path) - } - if pathExists { - return nil // file already exists, don't update it. - } - - // create the completion file - f, err = os.Create(path) - if err != nil { - lastErr = err - continue - } - break } } - if f == nil { - return lastErr - } - defer f.Close() - - compEntry, err := ZshCompleteEntry() + pathExists, err := fileutil.FileExists(path) if err != nil { - return errors.Wrapf(err, "Warning: unable to enable zsh-completion") - } - - _, err = f.Write([]byte(compEntry)) - if err != nil { - return errors.Wrapf(err, "failed writing to %s", path) + return errors.Wrapf(err, "failed to check if %s exists", path) } - if strings.HasPrefix(path, homeDir) { + if strings.HasPrefix(path, homeDir) && !pathExists { + // write when the path does not exist to prevent duplicate writing during updates. zshFile, err := os.OpenFile(fmt.Sprintf("%s/.zshrc", homeDir), os.O_RDWR|os.O_APPEND|os.O_CREATE, 0660) if err != nil { log.L.Warnf("unable to open the `~/.zshrc`, please add the following lines into `~/.zshrc` to get the envd zsh completion:\n"+ @@ -138,10 +115,19 @@ func InsertZSHCompleteEntry() error { } } + compEntry, err := ZshCompleteEntry(clicontext) + if err != nil { + return errors.Wrapf(err, "Warning: unable to enable zsh-completion") + } + + if err = os.WriteFile(path, []byte(compEntry), 0644); err != nil { + return errors.Wrapf(err, "failed writing to %s", path) + } + return deleteZcompdump() } -func ZshCompleteEntry() (string, error) { +func ZshCompleteEntry(_ *cli.Context) (string, error) { return autocompleteZSH, nil } diff --git a/pkg/builder/build.go b/pkg/builder/build.go index 6fe8511af..ac80c3465 100644 --- a/pkg/builder/build.go +++ b/pkg/builder/build.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ import ( "context" "io" "os" - "path/filepath" + "strings" "github.com/cockroachdb/errors" "github.com/docker/cli/cli/config" @@ -26,6 +26,7 @@ import ( "github.com/moby/buildkit/client/llb" "github.com/moby/buildkit/session" "github.com/moby/buildkit/session/auth/authprovider" + ocispecs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/sirupsen/logrus" "golang.org/x/sync/errgroup" @@ -37,6 +38,7 @@ import ( "github.com/tensorchord/envd/pkg/lang/version" "github.com/tensorchord/envd/pkg/progress/progresswriter" "github.com/tensorchord/envd/pkg/types" + "github.com/tensorchord/envd/pkg/util/buildkitutil" ) func New(ctx context.Context, opt Options) (Builder, error) { @@ -45,12 +47,21 @@ func New(ctx context.Context, opt Options) (Builder, error) { return nil, errors.Wrap(err, "failed to parse output") } + c, err := home.GetManager().ContextGetCurrent() + if err != nil { + return nil, errors.Wrap(err, "failed to get the current context") + } + logrus.WithField("entry", entries).Debug("getting exporter entry") // Build docker image by default if len(entries) == 0 { + exportType := client.ExporterDocker + if c.Builder == types.BuilderTypeMoby { + exportType = "moby" + } entries = []client.ExportEntry{ { - Type: client.ExporterDocker, + Type: exportType, }, } } else if len(entries) > 1 { @@ -76,13 +87,20 @@ func New(ctx context.Context, opt Options) (Builder, error) { GetDepsFilesHandler: vc.GetDefaultGraph().GetDepsFiles, } - c, err := home.GetManager().ContextGetCurrent() - if err != nil { - return nil, errors.Wrap(err, "failed to get the current context") - } - cli, err := buildkitd.NewClient(ctx, c.Builder, c.BuilderAddress, "") - if err != nil { - return nil, errors.Wrap(err, "failed to create buildkit client") + var cli buildkitd.Client + bc := buildkitutil.BuildkitConfig{} + if c.Builder == types.BuilderTypeMoby { + cli, err = buildkitd.NewMobyClient(ctx, + c.Builder, c.BuilderAddress, &bc) + if err != nil { + return nil, errors.Wrap(err, "failed to create moby buildkit client") + } + } else { + cli, err = buildkitd.NewClient(ctx, + c.Builder, c.BuilderAddress, &bc) + if err != nil { + return nil, errors.Wrap(err, "failed to create buildkit client") + } } b.Client = cli @@ -103,6 +121,14 @@ func (b generalBuilder) NumGPUs() int { return b.graph.GetNumGPUs() } +func (b generalBuilder) ShmSize() int { + return b.graph.GetShmSize() +} + +func (b generalBuilder) IsDev() bool { + return b.graph.IsDev() +} + func (b generalBuilder) Build(ctx context.Context, force bool) error { if !force && !b.checkIfNeedBuild(ctx) { return nil @@ -141,8 +167,11 @@ func (b generalBuilder) Interpret() error { } func (b generalBuilder) Compile(ctx context.Context) (*llb.Definition, error) { - envName := filepath.Base(b.BuildContextDir) - def, err := b.graph.Compile(ctx, envName, b.PubKeyPath) + platform, err := parsePlatform(b.Platform) + if err != nil { + return nil, err + } + def, err := b.graph.Compile(ctx, b.BuildContextDir, b.PubKeyPath, platform, b.Options.ProgressMode) if err != nil { return nil, errors.Wrap(err, "failed to compile build.envd") } @@ -176,8 +205,10 @@ func (b generalBuilder) imageConfig(ctx context.Context) (string, error) { env := b.graph.GetEnviron() user := b.graph.GetUser() + platform := b.graph.GetPlatform() + workingDir := b.graph.GetWorkingDir() - data, err := ImageConfigStr(labels, ports, ep, env, user) + data, err := ImageConfigStr(labels, ports, ep, env, user, workingDir, platform) if err != nil { return "", errors.Wrap(err, "failed to get image config") } @@ -197,7 +228,6 @@ func (b generalBuilder) build(ctx context.Context, pw progresswriter.Writer) err if err != nil { return errors.Wrap(err, "failed to parse export cache") } - // k := platforms.Format(platforms.DefaultSpec()) ctx, cancel := context.WithCancel(ctx) defer cancel() eg, ctx := errgroup.WithContext(ctx) @@ -208,7 +238,14 @@ func (b generalBuilder) build(ctx context.Context, pw progresswriter.Writer) err for _, entry := range b.entries { // Set up docker config auth. dockerConfig := config.LoadDefaultConfigFile(os.Stderr) - attachable := []session.Attachable{authprovider.NewDockerAuthProvider(dockerConfig)} + attachable := []session.Attachable{ + authprovider.NewDockerAuthProvider( + authprovider.DockerAuthProviderConfig{ + ConfigFile: dockerConfig, + TLSConfigs: map[string]*authprovider.AuthTLSConfig{}, + }, + ), + } b.logger.WithFields(logrus.Fields{ "type": entry.Type, }).Debug("build image with buildkit") @@ -228,22 +265,7 @@ func (b generalBuilder) build(ctx context.Context, pw progresswriter.Writer) err } } defer pipeW.Close() - solveOpt := client.SolveOpt{ - CacheExports: ce, - Exports: []client.ExportEntry{entry}, - LocalDirs: map[string]string{ - flag.FlagCacheDir: home.GetManager().CacheDir(), - flag.FlagBuildContext: b.BuildContextDir, - }, - Session: attachable, - } - if b.UseHTTPProxy { - solveOpt.FrontendAttrs = map[string]string{ - "build-arg:HTTPS_PROXY": os.Getenv("HTTPS_PROXY"), - "build-arg:HTTP_PROXY": os.Getenv("HTTP_PROXY"), - "build-arg:NO_PROXY": os.Getenv("NO_PROXY"), - } - } + solveOpt := constructSolveOpt(ce, &entry, b, attachable) _, err := b.Client.Build(ctx, solveOpt, "envd", b.BuildFunc(), pw.Status()) if err != nil { err = errors.Wrap(&BuildkitdErr{err: err}, "Buildkit error") @@ -263,38 +285,43 @@ func (b generalBuilder) build(ctx context.Context, pw progresswriter.Writer) err b.logger.Debug("loading image to docker host") if err := dockerClient.Load(ctx, pipeR, true); err != nil { err = errors.Wrap(err, "failed to load docker image") - b.logger.Error(err) + b.logger.WithError(err).Error() return err } b.logger.Debug("loaded docker image successfully") return nil }) default: - eg.Go(func() error { - solveOpt := client.SolveOpt{ - CacheExports: ce, - Exports: []client.ExportEntry{entry}, - LocalDirs: map[string]string{ - flag.FlagCacheDir: home.GetManager().CacheDir(), - flag.FlagBuildContext: b.BuildContextDir, - }, - Session: attachable, - } - if b.UseHTTPProxy { - solveOpt.FrontendAttrs = map[string]string{ - "build-arg:HTTPS_PROXY": os.Getenv("HTTPS_PROXY"), - "build-arg:HTTP_PROXY": os.Getenv("HTTP_PROXY"), - "build-arg:NO_PROXY": os.Getenv("NO_PROXY"), + func(entry client.ExportEntry) { + eg.Go(func() error { + solveOpt := constructSolveOpt(ce, &entry, b, attachable) + _, err := b.Client.Build(ctx, solveOpt, "envd", b.BuildFunc(), pw.Status()) + if err != nil { + err = errors.Wrap(err, "failed to solve LLB") + return err } - } - _, err := b.Client.Build(ctx, solveOpt, "envd", b.BuildFunc(), pw.Status()) - if err != nil { - err = errors.Wrap(err, "failed to solve LLB") - return err - } - b.logger.Debug("llb def is solved successfully") - return nil - }) + b.logger.Debug("llb def is solved successfully") + + // push the image if it's moby builder and push=true + if !(entry.Type == "moby" && entry.Attrs["push"] == "true") { + return nil + } + b.logger.Debug("pushing image to registry") + client, err := docker.NewClient(ctx) + if err != nil { + err = errors.Wrap(err, "failed to init docker client") + b.logger.WithError(err).Error() + return err + } + if err = client.PushImage(ctx, entry.Attrs["name"], b.Platform); err != nil { + err = errors.Wrap(err, "failed to push image") + b.logger.WithError(err).Error() + return err + } + b.logger.Debug("pushed image:", entry.Attrs["name"]) + return nil + }) + }(entry) } } @@ -312,10 +339,59 @@ func (b generalBuilder) build(ctx context.Context, pw progresswriter.Writer) err // Close the pipe on cancels, otherwise the whole thing hangs. pipeR.Close() return errors.Wrap(err, "build cancelled") - } else { - return errors.Wrap(err, "failed to wait error group") } + return errors.Wrap(err, "failed to wait error group") } b.logger.Debug("build successfully") return nil } + +func constructSolveOpt(ce []client.CacheOptionsEntry, entry *client.ExportEntry, + b generalBuilder, attachable []session.Attachable) client.SolveOpt { + c, _ := home.GetManager().ContextGetCurrent() + if c.Builder == types.BuilderTypeMoby { + if entry.Attrs == nil { + entry = &client.ExportEntry{ + Type: "moby", + Attrs: map[string]string{ + "name": b.Tag, + }, + } + } else if entry.Type == "image" { + entry.Type = "moby" + } + } + opt := client.SolveOpt{ + CacheExports: ce, + Exports: []client.ExportEntry{*entry}, + LocalDirs: map[string]string{ + flag.FlagCacheDir: home.GetManager().CacheDir(), + flag.FlagBuildContext: b.BuildContextDir, + }, + Session: attachable, + } + if b.UseHTTPProxy { + opt.FrontendAttrs = map[string]string{ + "build-arg:HTTPS_PROXY": os.Getenv("HTTPS_PROXY"), + "build-arg:HTTP_PROXY": os.Getenv("HTTP_PROXY"), + "build-arg:NO_PROXY": os.Getenv("NO_PROXY"), + } + } + return opt +} + +func parsePlatform(platform string) (*ocispecs.Platform, error) { + os, arch, variant := "linux", "amd64", "" + if platform == "" { + return &ocispecs.Platform{Architecture: arch, OS: os, Variant: variant}, nil + } + arr := strings.Split(platform, "/") + if len(arr) < 2 { + return nil, errors.New("invalid platform format, expected `os/arch[/variant]`") + } + os, arch = arr[0], arr[1] + if len(arr) >= 3 { + variant = arr[2] + } + return &ocispecs.Platform{Architecture: arch, OS: os, Variant: variant}, nil +} diff --git a/pkg/builder/build_func.go b/pkg/builder/build_func.go index 40d620c1b..21b13fd9a 100644 --- a/pkg/builder/build_func.go +++ b/pkg/builder/build_func.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/builder/builder.go b/pkg/builder/builder.go index bf0053e3e..832ac21be 100644 --- a/pkg/builder/builder.go +++ b/pkg/builder/builder.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -29,5 +29,6 @@ type Builder interface { Compile(ctx context.Context) (*llb.Definition, error) GPUEnabled() bool NumGPUs() int + ShmSize() int GetGraph() ir.Graph } diff --git a/pkg/builder/builder_suite_test.go b/pkg/builder/builder_suite_test.go index 15a711df9..87a04c0bf 100644 --- a/pkg/builder/builder_suite_test.go +++ b/pkg/builder/builder_suite_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/builder/builder_test.go b/pkg/builder/builder_test.go index db596cac6..a9e111bb8 100644 --- a/pkg/builder/builder_test.go +++ b/pkg/builder/builder_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -28,9 +28,10 @@ import ( mockbuildkitd "github.com/tensorchord/envd/pkg/buildkitd/mock" "github.com/tensorchord/envd/pkg/home" mockstarlark "github.com/tensorchord/envd/pkg/lang/frontend/starlark/mock" - v0 "github.com/tensorchord/envd/pkg/lang/ir/v0" + v1 "github.com/tensorchord/envd/pkg/lang/ir/v1" "github.com/tensorchord/envd/pkg/progress/compileui" compileuimock "github.com/tensorchord/envd/pkg/progress/compileui/mock" + progressmode "github.com/tensorchord/envd/pkg/progress/mode" "github.com/tensorchord/envd/pkg/progress/progresswriter" sshconfig "github.com/tensorchord/envd/pkg/ssh/config" ) @@ -56,7 +57,7 @@ var _ = Describe("Builder", func() { Options: Options{ ManifestFilePath: manifestFilePath, ConfigFilePath: configFilePath, - ProgressMode: "plain", + ProgressMode: progressmode.PLAIN, Tag: tag, BuildFuncName: "build", PubKeyPath: pub, @@ -70,7 +71,7 @@ var _ = Describe("Builder", func() { ctrlWriter := gomock.NewController(GinkgoT()) w = compileuimock.NewMockWriter(ctrlWriter) - v0.DefaultGraph.SetWriter(w) + v1.DefaultGraph.SetWriter(w) }) When("build error", func() { diff --git a/pkg/builder/dep_check.go b/pkg/builder/dep_check.go index 9dbbbc2ba..145ba3835 100644 --- a/pkg/builder/dep_check.go +++ b/pkg/builder/dep_check.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -31,7 +31,7 @@ func (b generalBuilder) checkIfNeedBuild(ctx context.Context) bool { depsFiles = b.GetDepsFilesHandler(depsFiles) isUpdated, err := b.checkDepsFileUpdate(ctx, b.Tag, b.ManifestFilePath, depsFiles) if err != nil { - b.logger.Debugf("failed to check manifest update: %s", err) + b.logger.WithError(err).Debug("failed to check manifest update") } if !isUpdated { b.logger.Infof("manifest is not updated, skip building") diff --git a/pkg/builder/err.go b/pkg/builder/err.go index 1a052dd2c..1b4056bd7 100644 --- a/pkg/builder/err.go +++ b/pkg/builder/err.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/builder/types.go b/pkg/builder/types.go index 52cb87f8f..9127c8a4f 100644 --- a/pkg/builder/types.go +++ b/pkg/builder/types.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -49,6 +49,9 @@ type Options struct { ImportCache string // UseHTTPProxy uses HTTPS_PROXY/HTTP_PROXY/NO_PROXY in the build process. UseHTTPProxy bool + // Specify the target platform for the build output. + // e.g. platform=linux/arm64,linux/amd64 + Platform string } type generalBuilder struct { diff --git a/pkg/builder/util.go b/pkg/builder/util.go index 49eafb084..d0d0b7929 100644 --- a/pkg/builder/util.go +++ b/pkg/builder/util.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -23,10 +23,9 @@ import ( "github.com/cockroachdb/errors" "github.com/containerd/console" - "github.com/containerd/containerd/platforms" "github.com/moby/buildkit/client" gatewayclient "github.com/moby/buildkit/frontend/gateway/client" - v1 "github.com/opencontainers/image-spec/specs-go/v1" + ocispecs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/sirupsen/logrus" ) @@ -36,21 +35,22 @@ const ( ) func ImageConfigStr(labels map[string]string, ports map[string]struct{}, - entrypoint []string, env []string, user string) (string, error) { - pl := platforms.Normalize(platforms.DefaultSpec()) - img := v1.Image{ - Config: v1.ImageConfig{ + entrypoint []string, env []string, user, workingDir string, platform *ocispecs.Platform) (string, error) { + img := ocispecs.Image{ + Config: ocispecs.ImageConfig{ Labels: labels, User: user, - WorkingDir: "/", + WorkingDir: workingDir, Env: env, ExposedPorts: ports, Entrypoint: entrypoint, }, - Architecture: pl.Architecture, // Refer to https://github.com/tensorchord/envd/issues/269#issuecomment-1152944914 - OS: "linux", - RootFS: v1.RootFS{ + Platform: ocispecs.Platform{ + Architecture: platform.Architecture, + OS: platform.OS, + }, + RootFS: ocispecs.RootFS{ Type: "layers", }, } diff --git a/pkg/builder/util_test.go b/pkg/builder/util_test.go index 77a045c06..fe99ffb97 100644 --- a/pkg/builder/util_test.go +++ b/pkg/builder/util_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/buildkitd/buildkitd.go b/pkg/buildkitd/buildkitd.go index bd4f4edde..ac5a9f7bf 100644 --- a/pkg/buildkitd/buildkitd.go +++ b/pkg/buildkitd/buildkitd.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,16 +17,19 @@ package buildkitd import ( "context" "fmt" + "net" "os" "text/tabwriter" "time" "github.com/cockroachdb/errors" + dockerclient "github.com/docker/docker/client" "github.com/moby/buildkit/client" "github.com/moby/buildkit/client/llb" gateway "github.com/moby/buildkit/frontend/gateway/client" "github.com/sirupsen/logrus" "github.com/spf13/viper" + "github.com/tonistiigi/units" "github.com/tensorchord/envd/pkg/driver" @@ -34,12 +37,14 @@ import ( "github.com/tensorchord/envd/pkg/driver/nerdctl" "github.com/tensorchord/envd/pkg/flag" "github.com/tensorchord/envd/pkg/types" + "github.com/tensorchord/envd/pkg/util/buildkitutil" + "github.com/tensorchord/envd/pkg/util/envutil" ) var ( interval = time.Second * 1 - timeoutConnection = time.Second * 5 - timeoutRun = time.Second * 3 + connectingTimeout = envutil.GetDurationWithDefault("BUILDKIT_CONNECTING_TIMEOUT", time.Second*5) + runningTimeout = envutil.GetDurationWithDefault("BUILDKIT_RUNNING_TIMEOUT", time.Second*3) ) // Client is a client for the buildkitd daemon. @@ -53,14 +58,14 @@ type Client interface { buildFunc gateway.BuildFunc, statusChan chan *client.SolveStatus, ) (*client.SolveResponse, error) Prune(ctx context.Context, keepDuration time.Duration, - keepStorage float64, filter []string, verbose, all bool) error + maxUsed float64, minFree float64, reserved float64, filter []string, verbose, all bool) error Close() error } type generalClient struct { - containerName string - image string - mirror string + containerName string + image string + buildkitConfig *buildkitutil.BuildkitConfig driver types.BuilderType socket string @@ -69,15 +74,49 @@ type generalClient struct { logger *logrus.Entry } +func NewMobyClient(ctx context.Context, driver types.BuilderType, + socket string, config *buildkitutil.BuildkitConfig) (Client, error) { + logrus.Debug("getting moby buildkit client") + c := &generalClient{ + containerName: socket, + image: viper.GetString(flag.FlagBuildkitdImage), + buildkitConfig: config, + socket: socket, + driver: driver, + } + c.logger = logrus.WithFields(logrus.Fields{ + "container": c.containerName, + "image": c.image, + "socket": c.socket, + "driver": c.driver, + }) + dockerCli, err := dockerclient.NewClientWithOpts(dockerclient.FromEnv, dockerclient.WithAPIVersionNegotiation()) + if err != nil { + return nil, errors.Wrap(err, "failed to create the client") + } + bkcli, err := client.New(ctx, c.BuildkitdAddr(), + client.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return dockerCli.DialHijack(ctx, "/grpc", "h2c", nil) + }), client.WithSessionDialer(func(ctx context.Context, proto string, meta map[string][]string) (net.Conn, error) { + return dockerCli.DialHijack(ctx, "/session", proto, meta) + }), + ) + if err != nil { + return nil, errors.Wrap(err, "failed to create buildkit client") + } + c.Client = bkcli + return c, nil +} + func NewClient(ctx context.Context, driver types.BuilderType, - socket, mirror string) (Client, error) { + socket string, config *buildkitutil.BuildkitConfig) (Client, error) { c := &generalClient{ - containerName: socket, - image: viper.GetString(flag.FlagBuildkitdImage), - mirror: mirror, + containerName: socket, + image: viper.GetString(flag.FlagBuildkitdImage), + buildkitConfig: config, + socket: socket, + driver: driver, } - c.socket = socket - c.driver = driver c.logger = logrus.WithFields(logrus.Fields{ "container": c.containerName, "image": c.image, @@ -85,13 +124,13 @@ func NewClient(ctx context.Context, driver types.BuilderType, "driver": c.driver, }) - cli, err := client.New(ctx, c.BuildkitdAddr(), client.WithFailFast()) + cli, err := client.New(ctx, c.BuildkitdAddr()) if err != nil { return nil, errors.Wrap(err, "failed to create the client") } c.Client = cli - if _, err := c.Bootstrap(ctx, timeoutRun, timeoutConnection); err != nil { + if _, err := c.Bootstrap(ctx, runningTimeout, connectingTimeout); err != nil { return nil, errors.Wrap(err, "failed to bootstrap the buildkitd") } return c, nil @@ -131,8 +170,7 @@ func (c *generalClient) maybeStart(ctx context.Context, } if client != nil { - if _, err := client.StartBuildkitd(ctx, - c.image, c.containerName, c.mirror, runningTimeout); err != nil { + if _, err := client.StartBuildkitd(ctx, c.image, c.containerName, c.buildkitConfig, runningTimeout); err != nil { return "", err } } @@ -159,7 +197,7 @@ func (c generalClient) waitUntilConnected( case <-time.After(interval): connected, err := c.connected(ctxTimeout) if err != nil { - logrus.Debugf("failed to connect to buildkitd: %s", err.Error()) + c.logger.WithError(err).Debug("failed to connect to buildkitd") continue } if !connected { @@ -184,10 +222,15 @@ func (c generalClient) connected(ctx context.Context) (bool, error) { } func (c generalClient) Prune(ctx context.Context, keepDuration time.Duration, - keepStorage float64, filter []string, verbose, all bool) error { + maxUsed float64, minFree float64, reserved float64, filter []string, verbose, all bool) error { opts := []client.PruneOption{ client.WithFilter(filter), - client.WithKeepOpt(keepDuration, int64(keepStorage*1e6)), + client.WithKeepOpt( + keepDuration, + int64(reserved*1e6), + int64(maxUsed*1e6), + int64(minFree*1e6), + ), } if all { opts = append(opts, client.PruneAll) diff --git a/pkg/buildkitd/mock/mock.go b/pkg/buildkitd/mock/mock.go index 5a34c7b95..a2612c350 100644 --- a/pkg/buildkitd/mock/mock.go +++ b/pkg/buildkitd/mock/mock.go @@ -82,17 +82,17 @@ func (mr *MockClientMockRecorder) Close() *gomock.Call { } // Prune mocks base method. -func (m *MockClient) Prune(ctx context.Context, keepDuration time.Duration, keepStorage float64, filter []string, verbose, all bool) error { +func (m *MockClient) Prune(ctx context.Context, keepDuration time.Duration, maxUsed, minFree, reserved float64, filter []string, verbose, all bool) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Prune", ctx, keepDuration, keepStorage, filter, verbose, all) + ret := m.ctrl.Call(m, "Prune", ctx, keepDuration, maxUsed, minFree, reserved, filter, verbose, all) ret0, _ := ret[0].(error) return ret0 } // Prune indicates an expected call of Prune. -func (mr *MockClientMockRecorder) Prune(ctx, keepDuration, keepStorage, filter, verbose, all interface{}) *gomock.Call { +func (mr *MockClientMockRecorder) Prune(ctx, keepDuration, maxUsed, minFree, reserved, filter, verbose, all interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Prune", reflect.TypeOf((*MockClient)(nil).Prune), ctx, keepDuration, keepStorage, filter, verbose, all) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Prune", reflect.TypeOf((*MockClient)(nil).Prune), ctx, keepDuration, maxUsed, minFree, reserved, filter, verbose, all) } // Solve mocks base method. diff --git a/pkg/buildkitd/print.go b/pkg/buildkitd/print.go index ef03c2355..6f75d4aff 100644 --- a/pkg/buildkitd/print.go +++ b/pkg/buildkitd/print.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/config/config.go b/pkg/config/config.go index cb8a55995..646dfb768 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,9 +1,10 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd 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 +// 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, diff --git a/pkg/data/datasource.go b/pkg/data/datasource.go index 92e7f05dc..84c89cc65 100644 --- a/pkg/data/datasource.go +++ b/pkg/data/datasource.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/data/envd.go b/pkg/data/envd.go index 7883a2432..82eee31a7 100644 --- a/pkg/data/envd.go +++ b/pkg/data/envd.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/driver/client.go b/pkg/driver/client.go index 4eabba577..f8c794d65 100644 --- a/pkg/driver/client.go +++ b/pkg/driver/client.go @@ -19,20 +19,22 @@ import ( "io" "time" - "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/image" + + "github.com/tensorchord/envd/pkg/util/buildkitutil" ) type Client interface { // Load loads the image from the reader to the docker host. Load(ctx context.Context, r io.ReadCloser, quiet bool) error - StartBuildkitd(ctx context.Context, tag, name, mirror string, timeout time.Duration) (string, error) + StartBuildkitd(ctx context.Context, tag, name string, config *buildkitutil.BuildkitConfig, timeout time.Duration) (string, error) Exec(ctx context.Context, cname string, cmd []string) error - GetImageWithCacheHashLabel(ctx context.Context, image string, hash string) (types.ImageSummary, error) + GetImageWithCacheHashLabel(ctx context.Context, image string, hash string) (image.Summary, error) RemoveImage(ctx context.Context, image string) error - - PruneImage(ctx context.Context) (types.ImagesPruneReport, error) + PushImage(ctx context.Context, image, platform string) error + PruneImage(ctx context.Context) (image.PruneReport, error) Stats(ctx context.Context, cname string, statChan chan<- *Stats, done <-chan bool) error } diff --git a/pkg/driver/docker/docker.go b/pkg/driver/docker/docker.go index de6192c25..d24a103da 100644 --- a/pkg/driver/docker/docker.go +++ b/pkg/driver/docker/docker.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,27 +15,42 @@ package docker import ( + "bytes" "context" + "encoding/base64" "encoding/json" "fmt" "io" "os" + "path/filepath" "regexp" + "strconv" "strings" "time" "github.com/cockroachdb/errors" - "github.com/docker/docker/api/types" + "github.com/containerd/errdefs" + "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/pkg/docker/config" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" + dockerimage "github.com/docker/docker/api/types/image" + "github.com/docker/docker/api/types/mount" "github.com/docker/docker/client" "github.com/docker/docker/pkg/jsonmessage" "github.com/moby/term" + imagespec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/sirupsen/logrus" "github.com/tensorchord/envd/pkg/driver" + "github.com/tensorchord/envd/pkg/envd" + containerType "github.com/tensorchord/envd/pkg/types" + "github.com/tensorchord/envd/pkg/util/buildkitutil" + "github.com/tensorchord/envd/pkg/util/fileutil" ) +const buildkitdConfigPath = "/etc/registry" + var ( anchoredIdentifierRegexp = regexp.MustCompile(`^([a-f0-9]{64})$`) waitingInterval = 1 * time.Second @@ -62,7 +77,7 @@ please visit https://docs.docker.com/engine/install/linux-postinstall/ for more return dockerClient{cli}, nil } -// Normalize the name accord the spec of docker, It may support normalize imagea and container in the future. +// Normalize the name accord the spec of docker, It may support normalize image and container in the future. func NormalizeName(s string) (string, error) { if ok := anchoredIdentifierRegexp.MatchString(s); ok { return "", errors.Newf("invalid repository name (%s), cannot specify 64-byte hexadecimal strings, please rename it", s) @@ -81,23 +96,26 @@ func NormalizeName(s string) (string, error) { } else { s = remoteName } - logrus.Warnf("The working direcotry's name is not lowercased: %s, the image built will be lowercased to %s", remoteName, s) + logrus.Warnf("The working directory's name is not lowercased: %s, the image built will be lowercased to %s", remoteName, s) } // remove the spaces s = strings.ReplaceAll(s, " ", "") - return s, nil - + name, err := reference.Parse(s) + if err != nil { + return "", errors.Wrapf(err, "failed to parse the name '%s', please provide a valid image name", s) + } + return name.String(), nil } -func (c dockerClient) ListImage(ctx context.Context) ([]types.ImageSummary, error) { - images, err := c.ImageList(ctx, types.ImageListOptions{ +func (c dockerClient) ListImage(ctx context.Context) ([]dockerimage.Summary, error) { + images, err := c.ImageList(ctx, dockerimage.ListOptions{ Filters: dockerFilters(false), }) return images, err } func (c dockerClient) RemoveImage(ctx context.Context, image string) error { - _, err := c.ImageRemove(ctx, image, types.ImageRemoveOptions{}) + _, err := c.ImageRemove(ctx, image, dockerimage.RemoveOptions{}) if err != nil { logrus.WithError(err).Errorf("failed to remove image %s", image) return err @@ -105,28 +123,87 @@ func (c dockerClient) RemoveImage(ctx context.Context, image string) error { return nil } -func (c dockerClient) GetImage(ctx context.Context, image string) (types.ImageSummary, error) { - images, err := c.ImageList(ctx, types.ImageListOptions{ +func (c dockerClient) PushImage(ctx context.Context, image string, platform string) error { + ref, err := reference.ParseNormalizedNamed(image) + if err != nil { + return errors.Wrap(err, "failed to normalize the image name") + } + auth, err := config.GetCredentialsForRef(nil, ref) + if err != nil { + return errors.Wrap(err, "failed to get credentials for image") + } + buf, err := json.Marshal(auth) + if err != nil { + return errors.Wrap(err, "failed to marshal auth struct") + } + platformInfo := strings.Split(platform, "/") + if len(platformInfo) != 2 { + return errors.New("invalid platform format, should be <architecture>/<os>") + } + reader, err := c.ImagePush(ctx, image, dockerimage.PushOptions{ + RegistryAuth: base64.URLEncoding.EncodeToString(buf), + Platform: &imagespec.Platform{ + Architecture: platformInfo[0], + OS: platformInfo[1], + }, + }) + if err != nil { + logrus.WithError(err).Errorf("failed to push image %s", image) + return err + } + + bar := envd.InitProgressBar(0) + + defer func() { + reader.Close() + bar.Finish() + }() + + decoder := json.NewDecoder(reader) + stats := new(jsonmessage.JSONMessage) + for err := decoder.Decode(stats); !errors.Is(err, io.EOF); err = decoder.Decode(stats) { + if err != nil { + return err + } + if stats.Error != nil { + return stats.Error + } + + if stats.Status != "" { + if stats.ID == "" { + bar.UpdateTitle(stats.Status) + } else { + bar.UpdateTitle(fmt.Sprintf("Pushing image => [%s] %s %s", stats.ID, stats.Status, stats.Progress)) + } + } + + stats = new(jsonmessage.JSONMessage) + } + return nil +} + +func (c dockerClient) GetImage(ctx context.Context, image string) (dockerimage.Summary, error) { + images, err := c.ImageList(ctx, dockerimage.ListOptions{ Filters: dockerFiltersWithName(image), }) if err != nil { - return types.ImageSummary{}, err + return dockerimage.Summary{}, err } if len(images) == 0 { - return types.ImageSummary{}, errors.Errorf("image %s not found", image) + return dockerimage.Summary{}, errors.Errorf("image %s not found", image) } return images[0], nil } -func (c dockerClient) GetImageWithCacheHashLabel(ctx context.Context, image string, hash string) (types.ImageSummary, error) { - images, err := c.ImageList(ctx, types.ImageListOptions{ +func (c dockerClient) GetImageWithCacheHashLabel(ctx context.Context, image string, hash string) (dockerimage.Summary, error) { + images, err := c.ImageList(ctx, dockerimage.ListOptions{ Filters: dockerFiltersWithCacheLabel(image, hash), }) if err != nil { - return types.ImageSummary{}, err + return dockerimage.Summary{}, err } if len(images) == 0 { - return types.ImageSummary{}, errors.Errorf("image with hash %s not found", hash) + return dockerimage.Summary{}, errors.Errorf("image with hash %s not found", hash) } return images[0], nil } @@ -169,22 +246,38 @@ func (c dockerClient) ResumeContainer(ctx context.Context, name string) (string, return name, nil } -func (c dockerClient) StartBuildkitd(ctx context.Context, - tag, name, mirror string, timeout time.Duration) (string, error) { +func (c dockerClient) RemoveContainer(ctx context.Context, name string) (string, error) { + logger := logrus.WithField("container", name) + err := c.ContainerRemove(ctx, name, container.RemoveOptions{}) + if err != nil { + errCause := errors.UnwrapAll(err).Error() + switch { + case strings.Contains(errCause, "No such container"): + logger.Debug("container is not found, there is no need to remove it") + return "", errors.New("container not found") + default: + return "", errors.Wrap(err, "failed to remove container") + } + } + return name, nil +} + +func (c dockerClient) StartBuildkitd(ctx context.Context, tag, name string, bc *buildkitutil.BuildkitConfig, timeout time.Duration) (string, error) { logger := logrus.WithFields(logrus.Fields{ - "tag": tag, - "container": name, - "mirror": mirror, + "tag": tag, + "container": name, + "buildkit-config": bc, }) logger.Debug("starting buildkitd") - if _, _, err := c.ImageInspectWithRaw(ctx, tag); err != nil { - if !client.IsErrNotFound(err) { + var buf bytes.Buffer + if _, err := c.ImageInspect(ctx, tag, client.ImageInspectWithRawResponse(&buf)); err != nil { + if !errdefs.IsNotFound(err) { return "", errors.Wrap(err, "failed to inspect image") } // Pull the image. logger.Debug("pulling image") - body, err := c.ImagePull(ctx, tag, types.ImagePullOptions{}) + body, err := c.ImagePull(ctx, tag, dockerimage.PullOptions{}) if err != nil { return "", errors.Wrap(err, "failed to pull image") } @@ -198,27 +291,41 @@ func (c dockerClient) StartBuildkitd(ctx context.Context, config := &container.Config{ Image: tag, } - if mirror != "" { - cfg := fmt.Sprintf(` -[registry."docker.io"] - mirrors = ["%s"]`, mirror) - config.Entrypoint = []string{ - "/bin/sh", - "-c", - fmt.Sprintf("mkdir /etc/buildkit && echo '%s' > /etc/buildkit/buildkitd.toml && buildkitd", cfg), - } - logger.Debugf("setting buildkit config: %s", cfg) - } hostConfig := &container.HostConfig{ Privileged: true, + AutoRemove: true, + Mounts: []mount.Mount{ + { + Type: mount.TypeBind, + Source: fileutil.DefaultConfigDir, + Target: buildkitdConfigPath, + }, + }, + } + + err := bc.Save() + if err != nil { + return "", errors.Wrap(err, "failed to generate buildkit config") + } + config.Entrypoint = []string{ + "buildkitd", "--config", filepath.Join(buildkitdConfigPath, "buildkitd.toml"), } created, _ := c.Exists(ctx, name) if created { - err := c.ContainerStart(ctx, name, types.ContainerStartOptions{}) + status, err := c.GetStatus(ctx, name) + if err != nil { + return name, errors.Wrap(err, "failed to get container status") + } + + err = c.handleContainerCreated(ctx, name, status, timeout) if err != nil { - return name, err + return name, errors.Wrap(err, "failed to handle container created condition") + } + + // When status is StatusDead/StatusRemoving, we need to create and start the container later(not to return directly). + if status != containerType.StatusDead && status != containerType.StatusRemoving { + return name, nil } - return name, nil } resp, err := c.ContainerCreate(ctx, config, hostConfig, nil, nil, name) if err != nil { @@ -229,7 +336,7 @@ func (c dockerClient) StartBuildkitd(ctx context.Context, logger.Warnf("run with warnings: %s", w) } - if err := c.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil { + if err := c.ContainerStart(ctx, resp.ID, container.StartOptions{}); err != nil { return "", errors.Wrap(err, "failed to start container") } @@ -249,7 +356,7 @@ func (c dockerClient) StartBuildkitd(ctx context.Context, func (c dockerClient) Exists(ctx context.Context, cname string) (bool, error) { _, err := c.ContainerInspect(ctx, cname) if err != nil { - if client.IsErrNotFound(err) { + if errdefs.IsNotFound(err) { return false, nil } return false, err @@ -260,7 +367,7 @@ func (c dockerClient) Exists(ctx context.Context, cname string) (bool, error) { func (c dockerClient) IsRunning(ctx context.Context, cname string) (bool, error) { container, err := c.ContainerInspect(ctx, cname) if err != nil { - if client.IsErrNotFound(err) { + if errdefs.IsNotFound(err) { return false, nil } return false, err @@ -268,10 +375,21 @@ func (c dockerClient) IsRunning(ctx context.Context, cname string) (bool, error) return container.State.Running, nil } +func (c dockerClient) GetStatus(ctx context.Context, cname string) (containerType.ContainerStatus, error) { + container, err := c.ContainerInspect(ctx, cname) + if err != nil { + if errdefs.IsNotFound(err) { + return "", nil + } + return "", err + } + return containerType.ContainerStatus(container.State.Status), nil +} + // Load loads the docker image from the reader into the docker host. // It's up to the caller to close the io.ReadCloser. func (c dockerClient) Load(ctx context.Context, r io.ReadCloser, quiet bool) error { - resp, err := c.ImageLoad(ctx, r, quiet) + resp, err := c.ImageLoad(ctx, r, client.ImageLoadWithQuiet(quiet)) if err != nil { return err } @@ -281,7 +399,7 @@ func (c dockerClient) Load(ctx context.Context, r io.ReadCloser, quiet bool) err } func (c dockerClient) Exec(ctx context.Context, cname string, cmd []string) error { - execConfig := types.ExecConfig{ + execConfig := container.ExecOptions{ Cmd: cmd, Detach: true, } @@ -290,15 +408,15 @@ func (c dockerClient) Exec(ctx context.Context, cname string, cmd []string) erro return err } execID := resp.ID - return c.ContainerExecStart(ctx, execID, types.ExecStartCheck{ + return c.ContainerExecStart(ctx, execID, container.ExecStartOptions{ Detach: true, }) } -func (c dockerClient) PruneImage(ctx context.Context) (types.ImagesPruneReport, error) { +func (c dockerClient) PruneImage(ctx context.Context) (dockerimage.PruneReport, error) { pruneReport, err := c.ImagesPrune(ctx, filters.Args{}) if err != nil { - return types.ImagesPruneReport{}, errors.Wrap(err, "failed to prune images") + return dockerimage.PruneReport{}, errors.Wrap(err, "failed to prune images") } return pruneReport, nil } @@ -381,3 +499,101 @@ func (c dockerClient) waitUntilRunning(ctx context.Context, } } } + +func (c dockerClient) waitUntilRemoved(ctx context.Context, + name string, timeout time.Duration) error { + logger := logrus.WithField("container", name) + logger.Debug("waiting to be removed") + + // Wait for the container to be removed + ctxTimeout, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + for { + select { + case <-time.After(waitingInterval): + exist, err := c.Exists(ctxTimeout, name) + if err != nil { + return errors.Wrap(err, "failed to check if container has been removed") + } + if !exist { + logger.Debug("the container has been removed") + return nil + } + case <-ctxTimeout.Done(): + container, err := c.ContainerInspect(ctx, name) + if err != nil { + logger.Debugf("failed to inspect container %s", name) + } + state, err := json.Marshal(container.State) + if err != nil { + logger.Debug("failed to marshal container state") + } + logger.Debugf("container state: %s", state) + return errors.Errorf("timeout %s: container can't be removed", timeout) + } + } +} + +func (c dockerClient) handleContainerCreated(ctx context.Context, + cname string, status containerType.ContainerStatus, timeout time.Duration) error { + logger := logrus.WithFields(logrus.Fields{ + "container": cname, + "status": status, + }) + + switch status { + case containerType.StatusPaused: + logger.Info("container was paused, unpause it now...") + if _, err := c.ResumeContainer(ctx, cname); err != nil { + logger.WithError(err).Error("can not run buildkitd") + return errors.Wrap(err, "failed to unpause container") + } + case containerType.StatusExited: + logger.Info("container exited, try to start it...") + if err := c.ContainerStart(ctx, cname, container.StartOptions{}); err != nil { + logger.WithError(err).Error("can not run buildkitd") + return errors.Wrap(err, "failed to start exited container") + } + case containerType.StatusDead: + logger.Info("container is dead, try to remove it...") + if err := c.ContainerRemove(ctx, cname, container.RemoveOptions{}); err != nil { + logger.WithError(err).Error("can not run buildkitd") + return errors.Wrap(err, "failed to remove container") + } + case containerType.StatusCreated: + logger.Info("container is being created") + if err := c.waitUntilRunning(ctx, cname, timeout); err != nil { + logger.WithError(err).Error("can not run buildkitd") + return errors.Wrap(err, "failed to start container") + } + case containerType.StatusRemoving: + logger.Info("container is being removed.") + if err := c.waitUntilRemoved(ctx, cname, timeout); err != nil { + logger.WithError(err).Error("can not run buildkitd") + return errors.Wrap(err, "failed to remove container") + } + } + // No process for StatusRunning + + return nil +} + +func GetDockerVersion() (int, error) { + + ctx := context.Background() + cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) + if err != nil { + return -1, err + } + defer cli.Close() + + info, err := cli.Info(ctx) + if err != nil { + return -1, err + } + version, err := strconv.Atoi(strings.Split(info.ServerVersion, ".")[0]) + if err != nil { + return -1, err + } + return version, nil +} diff --git a/pkg/driver/docker/docker_suite_test.go b/pkg/driver/docker/docker_suite_test.go index 00e29aa1c..0dfc156dd 100644 --- a/pkg/driver/docker/docker_suite_test.go +++ b/pkg/driver/docker/docker_suite_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/driver/docker/docker_test.go b/pkg/driver/docker/docker_test.go index a75adba9d..975a41fad 100644 --- a/pkg/driver/docker/docker_test.go +++ b/pkg/driver/docker/docker_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ import ( ) var _ = Describe("docker", func() { - When("given the a lowcase tag", func() { + When("given the a lowercase tag", func() { It("should return the tag identically", func() { tag := "test:test" newTag, err := NormalizeName(tag) diff --git a/pkg/driver/docker/label.go b/pkg/driver/docker/label.go index 9c2fce120..4ea193e1a 100644 --- a/pkg/driver/docker/label.go +++ b/pkg/driver/docker/label.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/driver/nerdctl/nerdctl.go b/pkg/driver/nerdctl/nerdctl.go index ce9303eaf..b8612b759 100644 --- a/pkg/driver/nerdctl/nerdctl.go +++ b/pkg/driver/nerdctl/nerdctl.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -24,16 +24,23 @@ import ( "time" "github.com/cockroachdb/errors" - "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + dockerimage "github.com/docker/docker/api/types/image" "github.com/sirupsen/logrus" "github.com/tensorchord/envd/pkg/driver" + containerType "github.com/tensorchord/envd/pkg/types" + "github.com/tensorchord/envd/pkg/util/buildkitutil" ) type nerdctlClient struct { bin string } +var ( + waitingInterval = 1 * time.Second +) + func NewClient(ctx context.Context) (driver.Client, error) { bin, err := exec.LookPath("nerdctl") if err != nil { @@ -63,13 +70,12 @@ func (nc *nerdctlClient) Load(ctx context.Context, r io.ReadCloser, quiet bool) return nil } -func (nc *nerdctlClient) StartBuildkitd(ctx context.Context, - tag, name, mirror string, timeout time.Duration) (string, error) { +func (nc *nerdctlClient) StartBuildkitd(ctx context.Context, tag, name string, bc *buildkitutil.BuildkitConfig, timeout time.Duration) (string, error) { logger := logrus.WithFields(logrus.Fields{ - "tag": tag, - "container": name, - "mirror": mirror, - "driver": "nerdctl", + "tag": tag, + "container": name, + "buildkit-config": bc, + "driver": "nerdctl", }) logger.Debug("starting buildkitd") @@ -80,28 +86,45 @@ func (nc *nerdctlClient) StartBuildkitd(ctx context.Context, } existed, _ := nc.containerExists(ctx, name) - if !existed { - buildkitdCmd := "buildkitd" - if mirror != "" { - cfg := fmt.Sprintf(` -[registry."docker.io"] - mirrors = ["%s"]`, mirror) - buildkitdCmd = fmt.Sprintf("mkdir /etc/buildkit && echo '%s' > /etc/buildkit/buildkitd.toml && buildkitd", cfg) - logger.Debugf("setting buildkit config: %s", cfg) + if existed { + status, err := nc.GetStatus(ctx, name) + if err != nil { + return "", errors.Wrap(err, "failed to get container status") } - out, err := nc.exec(ctx, nil, "run", "-d", - "--name", name, - "--privileged", - "--entrypoint", "sh", - tag, "-c", buildkitdCmd) + err = nc.handleContainerCreated(ctx, name, status, timeout) if err != nil { - logrus.Error("can not run buildkitd", out, err) - return "", errors.Wrap(err, "running buildkitd") + return "", errors.Wrap(err, "failed to handle container created condition") + } + + // When status is StatusDead/StatusRemoving, we nened to create and start the container later(not to return directly). + if status != containerType.StatusDead && status != containerType.StatusRemoving { + return name, nil } } - err := nc.waitUntilRunning(ctx, name, timeout) + buildkitdCmd := "buildkitd" + // TODO: support mirror CA keypair + if len(bc.Registries) > 0 { + cfg, err := bc.String() + if err != nil { + return "", errors.Wrap(err, "failed to generate buildkit config") + } + buildkitdCmd = fmt.Sprintf("mkdir /etc/buildkit && echo '%s' > /etc/buildkit/buildkitd.toml && buildkitd", cfg) + logger.Debugf("setting buildkit config: %s", cfg) + } + + out, err := nc.exec(ctx, "run", "-d", + "--name", name, + "--privileged", + "--entrypoint", "sh", + tag, "-c", buildkitdCmd) + if err != nil { + logger.WithError(err).Error("can not run buildkitd", out) + return "", errors.Wrap(err, "running buildkitd") + } + + err = nc.waitUntilRunning(ctx, name, timeout) return name, err } @@ -110,19 +133,30 @@ func (nc *nerdctlClient) Exec(ctx context.Context, cname string, cmd []string) e return nil } -func (nc *nerdctlClient) GetImageWithCacheHashLabel(ctx context.Context, image string, hash string) (types.ImageSummary, error) { - return types.ImageSummary{}, nil +func (nc *nerdctlClient) GetImageWithCacheHashLabel(ctx context.Context, image string, hash string) (dockerimage.Summary, error) { + return dockerimage.Summary{}, nil } func (nc *nerdctlClient) RemoveImage(ctx context.Context, image string) error { return nil } -func (nc *nerdctlClient) PruneImage(ctx context.Context) (types.ImagesPruneReport, error) { - return types.ImagesPruneReport{}, nil +func (nc *nerdctlClient) PushImage(ctx context.Context, image, platform string) error { + return nil +} +func (nc *nerdctlClient) PruneImage(ctx context.Context) (dockerimage.PruneReport, error) { + return dockerimage.PruneReport{}, nil } func (nc *nerdctlClient) Stats(ctx context.Context, cname string, statChan chan<- *driver.Stats, done <-chan bool) error { return nil } +func (nc *nerdctlClient) GetStatus(ctx context.Context, cname string) (containerType.ContainerStatus, error) { + container, err := nc.containerInspect(ctx, cname) + if err != nil { + return "", err + } + return containerType.ContainerStatus(container.State.Status), nil +} + // TODO(kweizh): use container engine to wrap docker and nerdctl func (nc *nerdctlClient) waitUntilRunning(ctx context.Context, name string, timeout time.Duration) error { @@ -134,8 +168,8 @@ func (nc *nerdctlClient) waitUntilRunning(ctx context.Context, defer cancel() for { select { - case <-time.After(time.Second): - _, err := nc.exec(ctx, nil, "start", name) + case <-time.After(waitingInterval): + _, err := nc.exec(ctx, "start", name) if err != nil { continue } @@ -165,36 +199,112 @@ func (nc *nerdctlClient) waitUntilRunning(ctx context.Context, } } +func (nc *nerdctlClient) waitUntilRemoved(ctx context.Context, + name string, timeout time.Duration) error { + logger := logrus.WithField("container", name) + logger.Debug("waiting to be removed") + + // Wait for the container to be removed + ctxTimeout, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + for { + select { + case <-time.After(waitingInterval): + exist, err := nc.containerExists(ctxTimeout, name) + if err != nil { + return errors.Wrap(err, "failed to check if container has been removed") + } + if !exist { + logger.Debug("the container has been removed") + return nil + } + case <-ctxTimeout.Done(): + container, err := nc.containerInspect(ctx, name) + if err != nil { + logger.Debugf("failed to inspect container %s", name) + } + state, err := json.Marshal(container.State) + if err != nil { + logger.Debug("failed to marshal container state") + } + logger.Debugf("container state: %s", state) + return errors.Errorf("timeout %s: container can't be removed", timeout) + } + } +} + +func (nc *nerdctlClient) handleContainerCreated(ctx context.Context, + cname string, status containerType.ContainerStatus, timeout time.Duration) error { + logger := logrus.WithFields(logrus.Fields{ + "container": cname, + "status": status, + }) + + switch status { + case containerType.StatusPaused: + logger.Info("container was paused, unpause it now...") + if out, err := nc.exec(ctx, "unpause", cname); err != nil { + logger.WithError(err).Error("can not run buildkitd", out) + return errors.Wrap(err, "failed to unpause container") + } + case containerType.StatusExited: + logger.Info("container exited, try to start it...") + if out, err := nc.exec(ctx, "start", cname); err != nil { + logger.WithError(err).Error("can not run buildkitd", out) + return errors.Wrap(err, "failed to start exited container") + } + case containerType.StatusDead: + logger.Info("container is dead, try to remove it...") + if out, err := nc.exec(ctx, "remove", cname); err != nil { + logger.WithError(err).Error("can not run buildkitd", out) + return errors.Wrap(err, "failed to remove container") + } + case containerType.StatusCreated: + logger.Info("container is being created.") + if err := nc.waitUntilRunning(ctx, cname, timeout); err != nil { + logger.WithError(err).Error("can not run buildkitd") + return errors.Wrap(err, "failed to start container") + } + case containerType.StatusRemoving: + // The remaining condition is StatusRemoving, we just need to wait. + logger.Info("container is being removed.") + if err := nc.waitUntilRemoved(ctx, cname, timeout); err != nil { + logger.WithError(err).Error("can not run buildkitd") + return errors.Wrap(err, "failed to remove container") + } + } + // No process for StatusRunning + + return nil +} + func (nc *nerdctlClient) containerExists(ctx context.Context, tag string) (bool, error) { _, err := nc.containerInspect(ctx, tag) return err == nil, err } -func (nc *nerdctlClient) containerInspect(ctx context.Context, tag string) (*types.ContainerJSON, error) { - out, err := nc.exec(ctx, nil, "inspect", tag) +func (nc *nerdctlClient) containerInspect(ctx context.Context, tag string) (*container.InspectResponse, error) { + out, err := nc.exec(ctx, "inspect", tag) if err != nil { - //TODO(kweizh): check not found + // TODO(kweizh): check not found return nil, err } - cs := []types.ContainerJSON{} + cs := []container.InspectResponse{} err = json.Unmarshal([]byte(out), &cs) if err != nil { - logrus.Error(cs, err) + logrus.WithError(err).Error(cs) return nil, err } return &(cs[0]), nil } -func (nc *nerdctlClient) exec(ctx context.Context, stdin io.Reader, args ...string) (string, error) { +func (nc *nerdctlClient) exec(ctx context.Context, args ...string) (string, error) { cmd := exec.CommandContext(ctx, nc.bin, args...) var out bytes.Buffer cmd.Stdout = &out - if stdin != nil { - cmd.Stdin = stdin - } err := cmd.Run() if err != nil { @@ -211,7 +321,7 @@ func (nc *nerdctlClient) imageInspect(ctx context.Context, tag string) error { cmd.Stdout = &out err := cmd.Run() if err != nil { - //TODO(kweizh): check not found + // TODO(kweizh): check not found return err } diff --git a/pkg/editor/vscode/types.go b/pkg/editor/vscode/types.go index f4d5d2eb5..8bf921e47 100644 --- a/pkg/editor/vscode/types.go +++ b/pkg/editor/vscode/types.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,8 +17,8 @@ package vscode import "fmt" const ( - vendorVSCodeTemplate = "https://%s.gallery.vsassets.io/_apis/public/gallery/publisher/%s/extension/%s/%s/assetbyname/Microsoft.VisualStudio.Services.VSIXPackage" - vendorOpenVSXTemplate = "https://open-vsx.org/api/%s/%s/latest" + vendorVSCodeTemplate = "https://%s.gallery.vsassets.io/_apis/public/gallery/publisher/%s/extension/%s/%s/assetbyname/Microsoft.VisualStudio.Services.VSIXPackage?targetPlatform=%s" + vendorOpenVSXTemplate = "https://open-vsx.org/api/%s/%s/latest?targetPlatform=%s" ) type MarketplaceVendor string @@ -31,12 +31,13 @@ const ( type Plugin struct { Publisher string Extension string + Platform string Version *string } func (p Plugin) String() string { if p.Version != nil { - return fmt.Sprintf("%s.%s-%s", p.Publisher, p.Extension, *p.Version) + return fmt.Sprintf("%s.%s-%s@%s", p.Publisher, p.Extension, *p.Version, p.Platform) } - return fmt.Sprintf("%s.%s", p.Publisher, p.Extension) + return fmt.Sprintf("%s.%s@%s", p.Publisher, p.Extension, p.Platform) } diff --git a/pkg/editor/vscode/util.go b/pkg/editor/vscode/util.go index fd9a7e962..867f7dc55 100644 --- a/pkg/editor/vscode/util.go +++ b/pkg/editor/vscode/util.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -22,13 +22,64 @@ import ( "strings" "github.com/cockroachdb/errors" + ocispecs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/sirupsen/logrus" ) +const ( + PLATFORM_WIN32_X64 = "win32-x64" + PLATFORM_WIN32_IA32 = "win32-ia32" + PLATFORM_WIN32_ARM64 = "win32-arm64" + PLATFORM_LINUX_X64 = "linux-x64" + PLATFORM_LINUX_ARM64 = "linux-arm64" + PLATFORM_LINUX_ARMHF = "linux-armhf" + PLATFORM_DARWIN_X64 = "darwin-x64" + PLATFORM_DARWIN_ARM64 = "darwin-arm64" + PLATFORM_ALPINE_X64 = "alpine-x64" +) + +func ConvertLLBPlatform(platform *ocispecs.Platform) (string, error) { + // Convert opencontainers style platform to VSCode extension style platform. + switch platform.OS { + case "windows": + switch platform.Architecture { + case "amd64": + return PLATFORM_WIN32_X64, nil + case "386": + return PLATFORM_WIN32_IA32, nil + case "arm64": + return PLATFORM_WIN32_ARM64, nil + } + case "linux": + switch platform.Architecture { + case "amd64": + return PLATFORM_LINUX_X64, nil + case "arm64": + return PLATFORM_LINUX_ARM64, nil + case "arm": + return PLATFORM_LINUX_ARMHF, nil + } + case "darwin": + switch platform.Architecture { + case "amd64": + return PLATFORM_DARWIN_X64, nil + case "arm64": + return PLATFORM_DARWIN_ARM64, nil + } + case "alpine": + switch platform.Architecture { + case "amd64": + return PLATFORM_ALPINE_X64, nil + } + } + + return "", errors.Errorf("unsupported platform: %s/%s", platform.OS, platform.Architecture) +} + func GetLatestVersionURL(p Plugin) (string, error) { // Auto-detect the version. // Refer to https://github.com/tensorchord/envd/issues/161#issuecomment-1129475975 - latestURL := fmt.Sprintf(vendorOpenVSXTemplate, p.Publisher, p.Extension) + latestURL := fmt.Sprintf(vendorOpenVSXTemplate, p.Publisher, p.Extension, p.Platform) resp, err := http.Get(latestURL) if err != nil { return "", errors.Wrap(err, "failed to get latest version") @@ -41,14 +92,17 @@ func GetLatestVersionURL(p Plugin) (string, error) { if err := json.NewDecoder(resp.Body).Decode(&jsonResp); err != nil { return "", errors.Wrap(err, "failed to decode response") } - if jsonResp["files"] == nil { - return "", errors.New("failed to get latest version: no files") + if jsonResp["downloads"] == nil { + return "", errors.New("failed to get latest version: no downloads") + } + downloads := jsonResp["downloads"].(map[string]interface{}) + if downloads["universal"] != nil { + return downloads["universal"].(string), nil } - files := jsonResp["files"].(map[string]interface{}) - if files["download"] == nil { - return "", errors.New("failed to get latest version: no download url") + if downloads[p.Platform] == nil { + return "", errors.Errorf("failed to get latest version: no target platform %s", p.Platform) } - return files["download"].(string), nil + return downloads[p.Platform].(string), nil } func ParsePlugin(p string) (*Plugin, error) { diff --git a/pkg/editor/vscode/vscode.go b/pkg/editor/vscode/vscode.go index 4a1758cda..e0243c8b3 100644 --- a/pkg/editor/vscode/vscode.go +++ b/pkg/editor/vscode/vscode.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -60,19 +60,19 @@ func NewClient(vendor MarketplaceVendor) (Client, error) { func (c generalClient) PluginPath(p Plugin) string { if p.Version != nil { - return fmt.Sprintf("%s.%s-%s/extension/", p.Publisher, p.Extension, *p.Version) + return fmt.Sprintf("%s.%s-%s@%s/extension/", p.Publisher, p.Extension, *p.Version, p.Platform) } - return fmt.Sprintf("%s.%s/extension/", p.Publisher, p.Extension) + return fmt.Sprintf("%s.%s@%s/extension/", p.Publisher, p.Extension, p.Platform) } func unzipPath(p Plugin) string { if p.Version != nil { - return fmt.Sprintf("%s/%s.%s-%s", home.GetManager().CacheDir(), - p.Publisher, p.Extension, *p.Version) + return fmt.Sprintf("%s/%s.%s-%s@%s", home.GetManager().CacheDir(), + p.Publisher, p.Extension, *p.Version, p.Platform) } - return fmt.Sprintf("%s/%s.%s", home.GetManager().CacheDir(), - p.Publisher, p.Extension) + return fmt.Sprintf("%s/%s.%s@%s", home.GetManager().CacheDir(), + p.Publisher, p.Extension, p.Platform) } // DownloadOrCache downloads or cache the plugin. @@ -93,23 +93,24 @@ func (c generalClient) DownloadOrCache(p Plugin) (bool, error) { } // TODO(gaocegege): Support version auto-detection. url = fmt.Sprintf(vendorVSCodeTemplate, - p.Publisher, p.Publisher, p.Extension, *p.Version) - filename = fmt.Sprintf("%s/%s.%s-%s.vsix", home.GetManager().CacheDir(), - p.Publisher, p.Extension, *p.Version) + p.Publisher, p.Publisher, p.Extension, *p.Version, p.Platform) + filename = fmt.Sprintf("%s/%s.%s-%s@%s.vsix", home.GetManager().CacheDir(), + p.Publisher, p.Extension, *p.Version, p.Platform) } else { var err error url, err = GetLatestVersionURL(p) if err != nil { return false, errors.Wrap(err, "failed to get latest version url") } - filename = fmt.Sprintf("%s/%s.%s.vsix", home.GetManager().CacheDir(), - p.Publisher, p.Extension) + filename = fmt.Sprintf("%s/%s.%s@%s.vsix", home.GetManager().CacheDir(), + p.Publisher, p.Extension, p.Platform) } logger := logrus.WithFields(logrus.Fields{ "publisher": p.Publisher, "extension": p.Extension, "version": p.Version, + "platform": p.Platform, "url": url, "file": filename, }) diff --git a/pkg/editor/vscode/vscode_suite_test.go b/pkg/editor/vscode/vscode_suite_test.go index 2dfd67109..6a8c96bd7 100644 --- a/pkg/editor/vscode/vscode_suite_test.go +++ b/pkg/editor/vscode/vscode_suite_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/editor/vscode/vscode_test.go b/pkg/editor/vscode/vscode_test.go index c0d0280f8..8f3d3eb8d 100644 --- a/pkg/editor/vscode/vscode_test.go +++ b/pkg/editor/vscode/vscode_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/envd/docker.go b/pkg/envd/docker.go index 0c4259b2d..6742fe04b 100644 --- a/pkg/envd/docker.go +++ b/pkg/envd/docker.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -19,21 +19,26 @@ import ( "encoding/json" "fmt" "path/filepath" + "runtime" "strconv" "strings" "time" "github.com/cockroachdb/errors" - dockertypes "github.com/docker/docker/api/types" + "github.com/containerd/errdefs" + "github.com/docker/cli/opts" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" + dockerimage "github.com/docker/docker/api/types/image" "github.com/docker/docker/api/types/mount" "github.com/docker/docker/client" "github.com/docker/go-connections/nat" + dockerutils "github.com/docker/go-units" "github.com/sirupsen/logrus" envdconfig "github.com/tensorchord/envd/pkg/config" "github.com/tensorchord/envd/pkg/lang/ir" + "github.com/tensorchord/envd/pkg/lang/version" "github.com/tensorchord/envd/pkg/ssh" sshconfig "github.com/tensorchord/envd/pkg/ssh/config" "github.com/tensorchord/envd/pkg/types" @@ -61,7 +66,7 @@ func dockerFiltersWithName(name string) filters.Args { } func (e dockerEngine) ListImage(ctx context.Context) ([]types.EnvdImage, error) { - images, err := e.ImageList(ctx, dockertypes.ImageListOptions{ + images, err := e.ImageList(ctx, dockerimage.ListOptions{ Filters: dockerFilters(false), }) if err != nil { @@ -80,7 +85,7 @@ func (e dockerEngine) ListImage(ctx context.Context) ([]types.EnvdImage, error) } func (e dockerEngine) GetEnvironment(ctx context.Context, env string) (*types.EnvdEnvironment, error) { - ctrs, err := e.ContainerList(ctx, dockertypes.ContainerListOptions{ + ctrs, err := e.ContainerList(ctx, container.ListOptions{ Filters: dockerFiltersWithName(env), }) if err != nil { @@ -100,7 +105,7 @@ func (e dockerEngine) GetEnvironment(ctx context.Context, env string) (*types.En func (e dockerEngine) ListEnvironment( ctx context.Context) ([]types.EnvdEnvironment, error) { - ctrs, err := e.ContainerList(ctx, dockertypes.ContainerListOptions{ + ctrs, err := e.ContainerList(ctx, container.ListOptions{ Filters: dockerFilters(false), }) if err != nil { @@ -169,7 +174,7 @@ func (e dockerEngine) ListImageDependency(ctx context.Context, image string) (*t "image": image, }) logger.Debug("getting dependencies") - images, err := e.ImageList(ctx, dockertypes.ImageListOptions{ + images, err := e.ImageList(ctx, dockerimage.ListOptions{ Filters: dockerFiltersWithName(image), }) if err != nil { @@ -187,6 +192,35 @@ func (e dockerEngine) ListImageDependency(ctx context.Context, image string) (*t return dep, nil } +func (e dockerEngine) getVerFromImageLabel(ctx context.Context, env string) (version.Getter, error) { + ctr, err := e.GetImage(ctx, env) + if err != nil { + return nil, errors.Wrapf(err, "failed to inspect image: %s", env) + } + ver, ok := ctr.Labels[types.ImageLabelSyntaxVer] + if !ok { + return version.NewByVersion("v1"), nil + } + return version.NewByVersion(ver), nil +} + +func (e dockerEngine) listEnvGeneralGraph(ctx context.Context, env string, g ir.Graph) (ir.Graph, error) { + ctr, err := e.GetImage(ctx, env) + if err != nil { + return nil, errors.Wrapf(err, "failed to inspect image: %s", env) + } + code, ok := ctr.Labels[types.GeneralGraphCode] + if !ok { + return nil, errors.Newf("failed to get runtime graph label from image: %s", env) + } + logrus.WithField("env", env).Debugf("general graph: %s", code) + newg, err := g.GeneralGraphFromLabel([]byte(code)) + if err != nil { + return nil, errors.Wrapf(err, "failed to create runtime graph from the image: %s", env) + } + return newg, err +} + func (e dockerEngine) ListEnvRuntimeGraph(ctx context.Context, env string) (*ir.RuntimeGraph, error) { ctr, err := e.ContainerInspect(ctx, env) if err != nil { @@ -256,13 +290,13 @@ func (e dockerEngine) CleanEnvdIfExists(ctx context.Context, name string, force if !created { return nil } - return e.ContainerRemove(ctx, name, dockertypes.ContainerRemoveOptions{Force: force}) + return e.ContainerRemove(ctx, name, container.RemoveOptions{Force: force}) } func (e dockerEngine) Exists(ctx context.Context, cname string) (bool, error) { _, err := e.ContainerInspect(ctx, cname) if err != nil { - if client.IsErrNotFound(err) { + if errdefs.IsNotFound(err) { return false, nil } return false, err @@ -273,7 +307,7 @@ func (e dockerEngine) Exists(ctx context.Context, cname string) (bool, error) { func (e dockerEngine) IsRunning(ctx context.Context, cname string) (bool, error) { container, err := e.ContainerInspect(ctx, cname) if err != nil { - if client.IsErrNotFound(err) { + if errdefs.IsNotFound(err) { return false, nil } return false, err @@ -317,10 +351,19 @@ func (e dockerEngine) StartEnvd(ctx context.Context, so StartOptions) (*StartRes logger := logrus.WithFields(logrus.Fields{ "tag": so.Image, "environment": so.EnvironmentName, - "gpu": so.NumGPU, + "gpu-set": so.GPUSet, + "shm": so.ShmSize, + "cpu": so.NumCPU, + "cpu-set": so.CPUSet, + "memory": so.NumMem, "build-context": so.BuildContext, }) - if so.NumGPU != 0 { + + bar := InitProgressBar(5) + defer bar.Finish() + bar.UpdateTitle("configure the environment") + + if len(so.GPUSet) > 0 { nvruntimeExists, err := e.GPUEnabled(ctx) if err != nil { return nil, errors.Wrap(err, "failed to check if nvidia-runtime is installed") @@ -346,12 +389,30 @@ func (e dockerEngine) StartEnvd(ctx context.Context, so StartOptions) (*StartRes base := fileutil.EnvdHomeDir(filepath.Base(so.BuildContext)) config.WorkingDir = base + var g ir.Graph + if so.Image == "" { + g = so.DockerSource.Graph + } else { + // Use specified image as the running image + getter, err := e.getVerFromImageLabel(ctx, so.Image) + if err != nil { + return nil, errors.Wrap(err, "failed to get the version from the image label") + } + defaultGraph := getter.GetDefaultGraph() + if err != nil { + return nil, errors.Wrap(err, "failed to get the graph from the image") + } + g, err = e.listEnvGeneralGraph(ctx, so.Image, defaultGraph) + if err != nil { + return nil, errors.Wrap(err, "failed to get the graph from the image") + } + so.DockerSource.Graph = g + } + if so.DockerSource == nil || so.DockerSource.Graph == nil { return nil, errors.New("failed to get the docker-specific options") } - g := so.DockerSource.Graph - mountOption := make([]mount.Mount, 0, len(so.DockerSource.MountOptions)+len(g.GetMount())+1) for _, option := range so.DockerSource.MountOptions { @@ -406,6 +467,30 @@ func (e dockerEngine) StartEnvd(ctx context.Context, so StartOptions) (*StartRes if so.ShmSize > 0 { hostConfig.ShmSize = int64(so.ShmSize) * 1024 * 1024 } + // resource + if len(so.NumCPU) > 0 { + cpu, err := strconv.ParseFloat(so.NumCPU, 64) + if err != nil { + logger.Infof("parse `cpu` error: %v, ignore this argument", err) + } else if runtime.GOOS == "windows" { + hostConfig.NanoCPUs = int64(cpu * 10e9) + } else { + // refer to https://docs.docker.com/config/containers/resource_constraints/#configure-the-default-cfs-scheduler + // CPU quota and CPU period only work for UNIX platform + hostConfig.CPUQuota = int64(cpu * 10e5) + } + } + if len(so.CPUSet) > 0 { + hostConfig.CpusetCpus = so.CPUSet + } + if len(so.NumMem) > 0 { + mem, err := dockerutils.RAMInBytes(so.NumMem) + if err != nil { + logger.WithError(err).Info("parse `memory` error, ignore this argument") + } else { + hostConfig.Memory = mem + } + } // Configure ssh port. natPort := nat.Port(fmt.Sprintf("%d/tcp", envdconfig.SSHPortInContainer)) @@ -477,9 +562,13 @@ func (e dockerEngine) StartEnvd(ctx context.Context, so StartOptions) (*StartRes } } - if so.NumGPU != 0 { + if len(so.GPUSet) > 0 { logger.Debug("GPU is enabled.") - hostConfig.DeviceRequests = deviceRequests(so.NumGPU) + hostConfig.DeviceRequests, err = deviceRequests(so.GPUSet) + if err != nil { + return nil, errors.Wrap(err, "failed to parse gpu-set flag") + + } } config.Labels = e.labels(g, so.EnvironmentName, @@ -491,6 +580,7 @@ func (e dockerEngine) StartEnvd(ctx context.Context, so StartOptions) (*StartRes }) logger.Debugf("starting %s container", so.EnvironmentName) + bar.UpdateTitle("create the environment") resp, err := e.ContainerCreate(ctx, config, hostConfig, nil, nil, so.EnvironmentName) if err != nil { return nil, errors.Wrap(err, "failed to create the container") @@ -500,12 +590,13 @@ func (e dockerEngine) StartEnvd(ctx context.Context, so StartOptions) (*StartRes logger.Warnf("run with warnings: %s", w) } + bar.UpdateTitle("start the environment") if err := e.ContainerStart( - ctx, resp.ID, dockertypes.ContainerStartOptions{}); err != nil { + ctx, resp.ID, container.StartOptions{}); err != nil { errCause := errors.UnwrapAll(err) // Hack to check if the port is already allocated. if strings.Contains(errCause.Error(), "port is already allocated") { - logrus.Debugf("failed to allocate the port: %s", err) + logger.WithError(err).Debug("failed to allocate the port") return nil, errors.New("port is already allocated in the host") } return nil, errors.Wrap(err, "failed to run the container") @@ -516,11 +607,13 @@ func (e dockerEngine) StartEnvd(ctx context.Context, so StartOptions) (*StartRes return nil, errors.Wrap(err, "failed to inspect the container") } + bar.UpdateTitle("wait for the environment to start") if err := e.WaitUntilRunning( ctx, container.Name, so.Timeout); err != nil { return nil, errors.Wrap(err, "failed to wait until the container is running") } + bar.UpdateTitle("attach the environment") result := &StartResult{ SSHPort: sshPortInHost, Address: container.NetworkSettings.IPAddress, @@ -559,14 +652,14 @@ func (e dockerEngine) Destroy(ctx context.Context, name string) (string, error) } } - if err := e.ContainerRemove(ctx, name, dockertypes.ContainerRemoveOptions{}); err != nil { + if err := e.ContainerRemove(ctx, name, container.RemoveOptions{}); err != nil { return "", errors.Wrap(err, "failed to remove the container") } - if _, err := e.ImageRemove(ctx, ctr.Image, dockertypes.ImageRemoveOptions{}); err != nil { + if _, err := e.ImageRemove(ctx, ctr.Image, dockerimage.RemoveOptions{}); err != nil { return "", errors.Errorf("remove image %s failed: %w", ctr.Image, err) } - logrus.Infof("image(%s) is destroyed", ctr.Image) + logger.Infof("image(%s) is destroyed", ctr.Image) return name, nil } @@ -619,21 +712,20 @@ func (e dockerEngine) GPUEnabled(ctx context.Context) (bool, error) { } else if strings.HasSuffix(info.KernelVersion, "WSL2") { logrus.Warn("We couldn't detect if your runtime support GPU on WSL2, we will continue to run your environment.") return true, nil - } else { - return false, nil } + return false, nil } -func (e dockerEngine) GetImage(ctx context.Context, image string) (types.EnvdImage, error) { - images, err := e.ImageList(ctx, dockertypes.ImageListOptions{ - Filters: dockerFiltersWithName(image), +func (e dockerEngine) GetImage(ctx context.Context, imageName string) (types.EnvdImage, error) { + images, err := e.ImageList(ctx, dockerimage.ListOptions{ + Filters: dockerFiltersWithName(imageName), }) if err != nil { return types.EnvdImage{}, err } if len(images) == 0 { return types.EnvdImage{}, - errors.Errorf("image %s not found", image) + errors.Errorf("image %s not found", imageName) } img, err := types.NewImageFromSummary(images[0]) if err != nil { @@ -642,31 +734,29 @@ func (e dockerEngine) GetImage(ctx context.Context, image string) (types.EnvdIma return *img, nil } -func (e dockerEngine) PruneImage(ctx context.Context) (dockertypes.ImagesPruneReport, error) { +func (e dockerEngine) PruneImage(ctx context.Context) (dockerimage.PruneReport, error) { pruneReport, err := e.ImagesPrune(ctx, filters.Args{}) if err != nil { - return dockertypes.ImagesPruneReport{}, errors.Wrap(err, "failed to prune images") + return dockerimage.PruneReport{}, errors.Wrap(err, "failed to prune images") } return pruneReport, nil } -func deviceRequests(count int) []container.DeviceRequest { - return []container.DeviceRequest{ - { - Driver: "nvidia", - Capabilities: [][]string{ - {"gpu"}, - {"nvidia"}, - {"compute"}, - {"compat32"}, - {"graphics"}, - {"utility"}, - {"video"}, - {"display"}, - }, - Count: count, - }, +func deviceRequests(value string) ([]container.DeviceRequest, error) { + if value == "" { + return nil, nil + } + if !strings.Contains(value, "capabilities=") { + value += ",\"capabilities=nvidia,compute,compat32,graphics,utility,video,display\"" + } + if !strings.Contains(value, "driver=") { + value += ",driver=nvidia" + } + opt := opts.GpuOpts{} + if err := opt.Set(value); err != nil { + return nil, err } + return opt.Value(), nil } func (e dockerEngine) labels(g ir.Graph, name string, diff --git a/pkg/envd/docker_test.go b/pkg/envd/docker_test.go new file mode 100644 index 000000000..32aa4b97f --- /dev/null +++ b/pkg/envd/docker_test.go @@ -0,0 +1,164 @@ +// Copyright 2024 The envd 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 envd + +import ( + "reflect" + "testing" + + "github.com/docker/docker/api/types/container" +) + +func TestDeviceRequests(t *testing.T) { + type args struct { + value string + } + tests := []struct { + name string + args args + want []container.DeviceRequest + wantErr bool + }{ + { + name: "device=1", + args: args{ + value: "device=1", + }, + want: []container.DeviceRequest{ + { + Count: 0, + DeviceIDs: []string{"1"}, + Driver: "nvidia", + Capabilities: [][]string{ + {"nvidia", "compute", "compat32", "graphics", "utility", "video", "display", "gpu"}}, + Options: make(map[string]string), + }, + }, + wantErr: false, + }, + { + name: "device=1,3", + args: args{ + value: "\"device=1,3\"", + }, + want: []container.DeviceRequest{ + { + Count: 0, + DeviceIDs: []string{"1", "3"}, + Driver: "nvidia", + Capabilities: [][]string{ + {"nvidia", "compute", "compat32", "graphics", "utility", "video", "display", "gpu"}}, + Options: make(map[string]string), + }, + }, + wantErr: false, + }, + { + name: "all", + args: args{ + value: "all", + }, + want: []container.DeviceRequest{ + { + Count: -1, + Driver: "nvidia", + Capabilities: [][]string{ + {"nvidia", "compute", "compat32", "graphics", "utility", "video", "display", "gpu"}}, + Options: make(map[string]string), + }, + }, + wantErr: false, + }, + { + name: "3", + args: args{ + value: "3", + }, + want: []container.DeviceRequest{ + { + Count: 3, + Driver: "nvidia", + Capabilities: [][]string{ + {"nvidia", "compute", "compat32", "graphics", "utility", "video", "display", "gpu"}}, + Options: make(map[string]string), + }, + }, + wantErr: false, + }, + { + name: "count=5", + args: args{ + value: "count=5", + }, + want: []container.DeviceRequest{ + { + Count: 5, + Driver: "nvidia", + Capabilities: [][]string{ + {"nvidia", "compute", "compat32", "graphics", "utility", "video", "display", "gpu"}}, + Options: make(map[string]string), + }, + }, + wantErr: false, + }, + { + name: "device=1,3 with driver", + args: args{ + value: "\"device=1,3\",\"driver=custom\"", + }, + want: []container.DeviceRequest{ + { + Count: 0, + DeviceIDs: []string{"1", "3"}, + Driver: "custom", + Capabilities: [][]string{ + {"nvidia", "compute", "compat32", "graphics", "utility", "video", "display", "gpu"}}, + Options: make(map[string]string), + }, + }, + wantErr: false, + }, + { + name: "device=1,3 with capabilities", + args: args{ + value: "\"device=1,3\",\"capabilities=nvidia\"", + }, + want: []container.DeviceRequest{ + { + Count: 0, + DeviceIDs: []string{"1", "3"}, + Driver: "nvidia", + Capabilities: [][]string{ + {"nvidia", "gpu"}, + }, + Options: make(map[string]string), + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := deviceRequests(tt.args.value) + if (err != nil) != tt.wantErr { + t.Errorf("deviceRequests() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("deviceRequests() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/envd/engine.go b/pkg/envd/engine.go index 6d02e13e6..ceaf4573b 100644 --- a/pkg/envd/engine.go +++ b/pkg/envd/engine.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ import ( "context" "time" - dockertypes "github.com/docker/docker/api/types" + dockerimage "github.com/docker/docker/api/types/image" "github.com/tensorchord/envd/pkg/lang/ir" sshconfig "github.com/tensorchord/envd/pkg/ssh/config" @@ -63,7 +63,7 @@ type ImageClient interface { ListImage(ctx context.Context) ([]types.EnvdImage, error) ListImageDependency(ctx context.Context, image string) (*types.Dependency, error) GetImage(ctx context.Context, image string) (types.EnvdImage, error) - PruneImage(ctx context.Context) (dockertypes.ImagesPruneReport, error) + PruneImage(ctx context.Context) (dockerimage.PruneReport, error) } type VersionClient interface { diff --git a/pkg/envd/envdserver.go b/pkg/envd/envdserver.go index 0df9ff07e..0e3d5b79c 100644 --- a/pkg/envd/envdserver.go +++ b/pkg/envd/envdserver.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ import ( "time" "github.com/cockroachdb/errors" - dockertypes "github.com/docker/docker/api/types" + dockerimage "github.com/docker/docker/api/types/image" "github.com/sirupsen/logrus" servertypes "github.com/tensorchord/envd-server/api/types" "github.com/tensorchord/envd-server/client" @@ -85,8 +85,8 @@ func (e *envdServerEngine) GetImage(ctx context.Context, image string) (types.En return *img, nil } -func (e envdServerEngine) PruneImage(ctx context.Context) (dockertypes.ImagesPruneReport, error) { - return dockertypes.ImagesPruneReport{}, errors.New("not implemented for envd-server") +func (e envdServerEngine) PruneImage(ctx context.Context) (dockerimage.PruneReport, error) { + return dockerimage.PruneReport{}, errors.New("not implemented for envd-server") } func (e *envdServerEngine) GetInfo(ctx context.Context) (*types.EnvdInfo, error) { @@ -185,7 +185,7 @@ func (e envdServerEngine) Attach(name, iface, privateKeyPath string, startResult } localAddress := fmt.Sprintf("%s:%d", "localhost", localPort) remoteAddress := fmt.Sprintf("%s:%d", "localhost", p.Port) - logrus.Infof("service \"%s\" is listening at %s\n", p.Name, localAddress) + logrus.Infof(`service "%s" is listening at %s\n`, p.Name, localAddress) go func() { if err := sshClient.LocalForward(localAddress, remoteAddress); err != nil { outputChannel <- errors.Wrap(err, "failed to forward to local port") @@ -285,6 +285,7 @@ func (e *envdServerEngine) StartEnvd(ctx context.Context, so StartOptions) (*Sta }, Spec: servertypes.EnvironmentSpec{ Image: so.Image, + Sync: so.EngineSource.EnvdServerSource.Sync, }, Resources: servertypes.ResourceSpec{ CPU: so.NumCPU, @@ -296,16 +297,21 @@ func (e *envdServerEngine) StartEnvd(ctx context.Context, so StartOptions) (*Sta } logrus.WithField("req", req).Debug("send request to create new env") + bar := InitProgressBar(3) + defer bar.Finish() + bar.UpdateTitle("create environment") resp, err := e.EnvironmentCreate(ctx, req) if err != nil { return nil, errors.Wrap(err, "failed to create the environment") } + bar.UpdateTitle("wait for the remote environment to start") if err := e.WaitUntilRunning( ctx, resp.Created.Name, so.Timeout); err != nil { return nil, errors.Wrap(err, "failed to wait until the container is running") } + bar.UpdateTitle("attach to the remote environment") result := &StartResult{ SSHPort: 2222, Address: "", diff --git a/pkg/envd/factory.go b/pkg/envd/factory.go index 72c54abe9..6f893f982 100644 --- a/pkg/envd/factory.go +++ b/pkg/envd/factory.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -68,14 +68,13 @@ func New(ctx context.Context, opt Options) (Engine, error) { Client: cli, Loginname: ac.Name, }, nil - } else { - cli, err := client.NewClientWithOpts( - client.FromEnv, client.WithAPIVersionNegotiation()) - if err != nil { - return nil, errors.Wrap(err, "failed to create the docker client") - } - return &dockerEngine{ - Client: cli, - }, nil } + cli, err := client.NewClientWithOpts( + client.FromEnv, client.WithAPIVersionNegotiation()) + if err != nil { + return nil, errors.Wrap(err, "failed to create the docker client") + } + return &dockerEngine{ + Client: cli, + }, nil } diff --git a/pkg/envd/types.go b/pkg/envd/types.go index 686d962b9..ce5fc0307 100644 --- a/pkg/envd/types.go +++ b/pkg/envd/types.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,8 +15,11 @@ package envd import ( + "fmt" "time" + "github.com/schollz/progressbar/v3" + "github.com/sirupsen/logrus" "github.com/tensorchord/envd-server/api/types" "github.com/tensorchord/envd/pkg/lang/ir" @@ -35,7 +38,9 @@ type StartOptions struct { EnvironmentName string BuildContext string NumGPU int + GPUSet string NumCPU string + CPUSet string NumMem string Timeout time.Duration ShmSize int @@ -55,7 +60,9 @@ type DockerSource struct { MountOptions []string } -type EnvdServerSource struct{} +type EnvdServerSource struct { + Sync bool +} type StartResult struct { // TODO(gaocegege): Make result a chan, to send running status to the receiver. @@ -65,3 +72,62 @@ type StartResult struct { Ports []types.EnvironmentPort } + +type ProgressBar struct { + bar *progressbar.ProgressBar + currentStage int + totalStage int + notify chan struct{} +} + +// InitProgressBar initializes a progress bar. +// If stage <= 0, the progress bar will not show the (current/total) stage information. +func InitProgressBar(stage int) *ProgressBar { + done := make(chan struct{}) + bar := progressbar.NewOptions(-1, + progressbar.OptionSpinnerType(11), + progressbar.OptionEnableColorCodes(true), + progressbar.OptionOnCompletion(func() { + fmt.Println() + }), + ) + + go func() { + timer := time.NewTicker(time.Millisecond * 100) + for { + select { + case <-done: + return + case <-timer.C: + _ = bar.Add(1) + } + } + }() + + b := ProgressBar{ + notify: done, + bar: bar, + totalStage: stage, + } + return &b +} + +func (b *ProgressBar) UpdateTitle(title string) { + if b.totalStage > 0 { + b.currentStage += 1 + b.bar.Describe(fmt.Sprintf("[cyan][%d/%d][reset] %s", + b.currentStage, + b.totalStage, + title, + )) + } else { + b.bar.Describe(fmt.Sprintf("[cyan]%s[reset]", title)) + } +} + +func (b *ProgressBar) Finish() { + b.notify <- struct{}{} + if err := b.bar.Finish(); err != nil { + logrus.Infof("stop progress bar err: %v\n", err) + } +} diff --git a/pkg/flag/consts.go b/pkg/flag/consts.go index be26979fd..df9951004 100644 --- a/pkg/flag/consts.go +++ b/pkg/flag/consts.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/home/auth.go b/pkg/home/auth.go index f4f153f61..e72f8875e 100644 --- a/pkg/home/auth.go +++ b/pkg/home/auth.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -33,7 +33,7 @@ type authManager interface { } func (m *generalManager) initAuth() error { - // Create $HOME/.config/envd/auth.json + // Create $HOME/.config/envd/auth auth, err := fileutil.ConfigFile("auth") if err != nil { return errors.Wrap(err, "failed to get auth file") @@ -43,22 +43,21 @@ func (m *generalManager) initAuth() error { _, err = os.Stat(m.authFile) if err != nil { - if os.IsNotExist(err) { - logrus.WithField("filename", m.authFile).Debug("Creating file") - file, err := os.Create(m.authFile) - if err != nil { - return errors.Wrap(err, "failed to create file") - } - err = file.Close() - if err != nil { - return errors.Wrap(err, "failed to close file") - } - if err := m.dumpAuth(); err != nil { - return errors.Wrap(err, "failed to dump auth") - } - } else { + if !os.IsNotExist(err) { return errors.Wrap(err, "failed to stat file") } + logrus.WithField("filename", m.authFile).Debug("Creating file") + file, err := os.Create(m.authFile) + if err != nil { + return errors.Wrap(err, "failed to create file") + } + err = file.Close() + if err != nil { + return errors.Wrap(err, "failed to close file") + } + if err := m.dumpAuth(); err != nil { + return errors.Wrap(err, "failed to dump auth") + } } file, err := os.Open(m.authFile) diff --git a/pkg/home/auth_test.go b/pkg/home/auth_test.go index 464c795fe..73afefce2 100644 --- a/pkg/home/auth_test.go +++ b/pkg/home/auth_test.go @@ -1,16 +1,17 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd 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 +// 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 home import ( @@ -23,10 +24,10 @@ import ( var _ = Describe("auth test", Ordered, func() { defaultAuthName := "auth_name" - deafultJWTToken := "default_token" + defaultJWTToken := "default_token" ac := types.AuthConfig{ Name: defaultAuthName, - JWTToken: deafultJWTToken, + JWTToken: defaultJWTToken, } BeforeEach(func() { diff --git a/pkg/home/cache.go b/pkg/home/cache.go index d11daed25..a3d53f83c 100644 --- a/pkg/home/cache.go +++ b/pkg/home/cache.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -42,22 +42,21 @@ func (m *generalManager) initCache() error { m.cacheStatusFile = cacheStatusFile _, err = os.Stat(m.cacheStatusFile) if err != nil { - if os.IsNotExist(err) { - logrus.WithField("filename", m.cacheStatusFile).Debug("Creating file") - file, err := os.Create(m.cacheStatusFile) - if err != nil { - return errors.Wrap(err, "failed to create file") - } - err = file.Close() - if err != nil { - return errors.Wrap(err, "failed to close file") - } - if err := m.dumpCacheStatus(); err != nil { - return errors.Wrap(err, "failed to dump cache status") - } - } else { + if !os.IsNotExist(err) { return errors.Wrap(err, "failed to stat file") } + logrus.WithField("filename", m.cacheStatusFile).Debug("Creating file") + file, err := os.Create(m.cacheStatusFile) + if err != nil { + return errors.Wrap(err, "failed to create file") + } + err = file.Close() + if err != nil { + return errors.Wrap(err, "failed to close file") + } + if err := m.dumpCacheStatus(); err != nil { + return errors.Wrap(err, "failed to dump cache status") + } } file, err := os.Open(m.cacheStatusFile) @@ -72,12 +71,12 @@ func (m *generalManager) initCache() error { return nil } -func (m generalManager) MarkCache(key string, cached bool) error { +func (m *generalManager) MarkCache(key string, cached bool) error { m.cacheMap[key] = cached return m.dumpCacheStatus() } -func (m generalManager) Cached(key string) bool { +func (m *generalManager) Cached(key string) bool { return m.cacheMap[key] } @@ -95,11 +94,11 @@ func (m *generalManager) dumpCacheStatus() error { return nil } -func (m generalManager) CacheDir() string { +func (m *generalManager) CacheDir() string { return m.cacheDir } -func (m generalManager) CleanCache() error { +func (m *generalManager) CleanCache() error { if m.cacheDir == "" { return nil } diff --git a/pkg/home/config.go b/pkg/home/config.go index cacf4c4db..22dfcd218 100644 --- a/pkg/home/config.go +++ b/pkg/home/config.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -38,6 +38,6 @@ func (m *generalManager) initConfig() error { return nil } -func (m generalManager) ConfigFile() string { +func (m *generalManager) ConfigFile() string { return m.configFile } diff --git a/pkg/home/context.go b/pkg/home/context.go index 423f4181e..4b4039649 100644 --- a/pkg/home/context.go +++ b/pkg/home/context.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -45,22 +45,21 @@ func (m *generalManager) initContext() error { _, err = os.Stat(m.contextFile) if err != nil { - if os.IsNotExist(err) { - logrus.WithField("filename", m.contextFile).Debug("Creating file") - file, err := os.Create(m.contextFile) - if err != nil { - return errors.Wrap(err, "failed to create file") - } - err = file.Close() - if err != nil { - return errors.Wrap(err, "failed to close file") - } - if err := m.dumpContext(); err != nil { - return errors.Wrap(err, "failed to dump context") - } - } else { + if !os.IsNotExist(err) { return errors.Wrap(err, "failed to stat file") } + logrus.WithField("filename", m.contextFile).Debug("Creating file") + file, err := os.Create(m.contextFile) + if err != nil { + return errors.Wrap(err, "failed to create file") + } + err = file.Close() + if err != nil { + return errors.Wrap(err, "failed to close file") + } + if err := m.dumpContext(); err != nil { + return errors.Wrap(err, "failed to dump context") + } } file, err := os.Open(m.contextFile) @@ -75,11 +74,11 @@ func (m *generalManager) initContext() error { return nil } -func (m generalManager) ContextFile() string { +func (m *generalManager) ContextFile() string { return m.contextFile } -func (m generalManager) ContextGetCurrent() (*types.Context, error) { +func (m *generalManager) ContextGetCurrent() (*types.Context, error) { for _, c := range m.context.Contexts { if m.context.Current == c.Name { return &c, nil @@ -96,6 +95,7 @@ func (m *generalManager) ContextCreate(ctx types.Context, use bool) error { } switch ctx.Builder { case types.BuilderTypeDocker, + types.BuilderTypeMoby, types.BuilderTypeNerdctl, types.BuilderTypeKubernetes, types.BuilderTypeUNIXDomainSocket, @@ -131,7 +131,7 @@ func (m *generalManager) ContextRemove(name string) error { return errors.Newf("cannot find context \"%s\"", name) } -func (m generalManager) ContextList() (types.EnvdContext, error) { +func (m *generalManager) ContextList() (types.EnvdContext, error) { return m.context, nil } diff --git a/pkg/home/context_test.go b/pkg/home/context_test.go index 9b6354965..d8da279e3 100644 --- a/pkg/home/context_test.go +++ b/pkg/home/context_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/home/data.go b/pkg/home/data.go index 9859465d3..d8c51a66a 100644 --- a/pkg/home/data.go +++ b/pkg/home/data.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/home/home_suite_test.go b/pkg/home/home_suite_test.go index 373061f87..525f50adb 100644 --- a/pkg/home/home_suite_test.go +++ b/pkg/home/home_suite_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/home/manager.go b/pkg/home/manager.go index 518c8ac42..91534d9f1 100644 --- a/pkg/home/manager.go +++ b/pkg/home/manager.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -61,7 +61,7 @@ func Initialize() error { Contexts: []types.Context{ { Name: "default", - Builder: types.BuilderTypeDocker, + Builder: types.BuilderTypeMoby, BuilderAddress: "envd_buildkitd", Runner: types.RunnerTypeDocker, RunnerAddress: nil, diff --git a/pkg/home/manager_test.go b/pkg/home/manager_test.go index 2a8d96432..2263d6337 100644 --- a/pkg/home/manager_test.go +++ b/pkg/home/manager_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -48,7 +48,7 @@ var _ = Describe("home manager", func() { Expect(m.ContextFile()).To(Equal(filepath.Join(fileutil.DefaultConfigDir, "contexts"))) c, err := m.ContextGetCurrent() Expect(err).NotTo(HaveOccurred()) - Expect(c.Builder).To(Equal(types.BuilderTypeDocker)) + Expect(c.Builder).To(Or(Equal(types.BuilderTypeDocker), Equal(types.BuilderTypeMoby))) Expect(c.BuilderAddress).To(Equal("envd_buildkitd")) Expect(c.Runner).To(Equal(types.RunnerTypeDocker)) }) diff --git a/pkg/lang/frontend/starlark/interpreter.go b/pkg/lang/frontend/starlark/interpreter.go index 2265bdc20..eedc5547c 100644 --- a/pkg/lang/frontend/starlark/interpreter.go +++ b/pkg/lang/frontend/starlark/interpreter.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/lang/frontend/starlark/v0/builtin/builtin.go b/pkg/lang/frontend/starlark/v0/builtin/builtin.go deleted file mode 100644 index 148ba725c..000000000 --- a/pkg/lang/frontend/starlark/v0/builtin/builtin.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2022 The envd 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 builtin - -const ( - // BuildContextDir is the name of the directory that contains the build context. - BuildContextDir = "_build_context_dir" -) diff --git a/pkg/lang/frontend/starlark/v0/config/config.go b/pkg/lang/frontend/starlark/v0/config/config.go deleted file mode 100644 index adc275c96..000000000 --- a/pkg/lang/frontend/starlark/v0/config/config.go +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright 2022 The envd 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 config - -import ( - "github.com/cockroachdb/errors" - "github.com/sirupsen/logrus" - "go.starlark.net/starlark" - "go.starlark.net/starlarkstruct" - - ir "github.com/tensorchord/envd/pkg/lang/ir/v0" - "github.com/tensorchord/envd/pkg/util/starlarkutil" -) - -var ( - logger = logrus.WithField("frontend", "starlark") -) - -var Module = &starlarkstruct.Module{ - Name: "config", - Members: starlark.StringDict{ - "apt_source": starlark.NewBuiltin(ruleUbuntuAptSource, ruleFuncUbuntuAptSource), - "gpu": starlark.NewBuiltin(ruleGPU, ruleFuncGPU), - "jupyter": starlark.NewBuiltin(ruleJupyter, ruleFuncJupyter), - "cran_mirror": starlark.NewBuiltin( - ruleCRANMirror, ruleFuncCRANMirror), - "pip_index": starlark.NewBuiltin( - rulePyPIIndex, ruleFuncPyPIIndex), - "conda_channel": starlark.NewBuiltin( - ruleCondaChannel, ruleFuncCondaChannel), - "julia_pkg_server": starlark.NewBuiltin( - ruleJuliaPackageServer, ruleFuncJuliaPackageServer), - "rstudio_server": starlark.NewBuiltin(ruleRStudioServer, ruleFuncRStudioServer), - "entrypoint": starlark.NewBuiltin(ruleEntrypoint, ruleFuncEntrypoint), - "repo": starlark.NewBuiltin(ruleRepo, ruleFuncRepo), - }, -} - -func ruleFuncGPU(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var numGPUs starlark.Int - - if err := starlark.UnpackArgs(ruleGPU, args, kwargs, - "count?", &numGPUs); err != nil { - return nil, err - } - - numGPUsInt, ok := numGPUs.Int64() - if ok { - ir.GPU(int(numGPUsInt)) - logger.Debugf("Using %d GPUs", int(numGPUsInt)) - } else { - logger.Debugf("Failed to convert gpu count to int64") - } - return starlark.None, nil -} - -func ruleFuncJupyter(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var token starlark.String - var port starlark.Int - - if err := starlark.UnpackArgs(ruleJupyter, args, kwargs, - "token?", &token, "port?", &port); err != nil { - return nil, err - } - - pwdStr := token.GoString() - - portInt, ok := port.Int64() - if !ok { - return nil, errors.New("port must be an integer") - } - logger.Debugf("rule `%s` is invoked, password=%s, port=%d", - ruleJupyter, pwdStr, portInt) - if err := ir.Jupyter(pwdStr, portInt); err != nil { - return nil, err - } - - return starlark.None, nil -} - -func ruleFuncPyPIIndex(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var url, extraURL starlark.String - var trust bool - - if err := starlark.UnpackArgs(rulePyPIIndex, args, kwargs, - "url?", &url, "extra_url?", &extraURL, "trust?", &trust); err != nil { - return nil, err - } - - indexStr := url.GoString() - extraIndexStr := extraURL.GoString() - - logger.Debugf("rule `%s` is invoked, index=%s, extraIndex=%s, trust=%t", - rulePyPIIndex, indexStr, extraIndexStr, trust) - if err := ir.PyPIIndex(indexStr, extraIndexStr, trust); err != nil { - return nil, err - } - - return starlark.None, nil -} - -func ruleFuncCRANMirror(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var url starlark.String - - if err := starlark.UnpackArgs(ruleCRANMirror, args, kwargs, - "url?", &url); err != nil { - return nil, err - } - - urlStr := url.GoString() - - logger.Debugf("rule `%s` is invoked, url=%s", ruleCRANMirror, urlStr) - if err := ir.CRANMirror(urlStr); err != nil { - return nil, err - } - return starlark.None, nil -} - -func ruleFuncJuliaPackageServer(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var url starlark.String - - if err := starlark.UnpackArgs(ruleJuliaPackageServer, args, kwargs, - "url?", &url); err != nil { - return nil, err - } - - urlStr := url.GoString() - - logger.Debugf("rule `%s` is invoked, url=%s", ruleJuliaPackageServer, urlStr) - if err := ir.JuliaPackageServer(urlStr); err != nil { - return nil, err - } - return starlark.None, nil -} - -func ruleFuncUbuntuAptSource(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var source starlark.String - - if err := starlark.UnpackArgs(ruleUbuntuAptSource, args, kwargs, - "source?", &source); err != nil { - return nil, err - } - - sourceStr := source.GoString() - - logger.Debugf("rule `%s` is invoked, source=%s", ruleUbuntuAptSource, sourceStr) - if err := ir.UbuntuAPT(sourceStr); err != nil { - return nil, err - } - - return starlark.None, nil -} - -func ruleFuncRStudioServer(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - if err := ir.RStudioServer(); err != nil { - return nil, err - } - - return starlark.None, nil -} - -func ruleFuncCondaChannel(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var channel string - var useMamba bool - - if err := starlark.UnpackArgs(ruleCondaChannel, args, kwargs, - "channel?", &channel, "use_mamba?", &useMamba); err != nil { - return nil, err - } - - logger.Debugf("rule `%s` is invoked, channel=%s, use_mamba=%t\n", - ruleCondaChannel, channel, useMamba) - if err := ir.CondaChannel(channel, useMamba); err != nil { - return nil, err - } - - return starlark.None, nil -} - -func ruleFuncEntrypoint(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var argv *starlark.List - - if err := starlark.UnpackArgs(ruleEntrypoint, args, kwargs, "args", &argv); err != nil { - return nil, err - } - - argList, err := starlarkutil.ToStringSlice(argv) - if err != nil { - return nil, err - } - - logger.Debugf("user defined entrypoints: {%s}\n", argList) - ir.Entrypoint(argList) - return starlark.None, nil -} - -func ruleFuncRepo(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var url, description string - - if err := starlark.UnpackArgs(ruleRepo, args, kwargs, "url", &url, "description?", &description); err != nil { - return nil, err - } - - logger.Debugf("repo info: url=%s, description=%s", url, description) - ir.Repo(url, description) - return starlark.None, nil -} diff --git a/pkg/lang/frontend/starlark/v0/config/const.go b/pkg/lang/frontend/starlark/v0/config/const.go deleted file mode 100644 index 3e084f794..000000000 --- a/pkg/lang/frontend/starlark/v0/config/const.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2022 The envd 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 config - -const ( - ruleUbuntuAptSource = "config.apt_source" - rulePyPIIndex = "config.pip_index" - ruleCRANMirror = "config.cran_mirror" - ruleJupyter = "config.jupyter" - ruleCondaChannel = "config.conda_channel" - ruleGPU = "config.gpu" - ruleJuliaPackageServer = "config.julia_pkg_server" - ruleRStudioServer = "config.rstudio_server" - ruleEntrypoint = "config.entrypoint" - ruleRepo = "config.repo" -) diff --git a/pkg/lang/frontend/starlark/v0/data/const.go b/pkg/lang/frontend/starlark/v0/data/const.go deleted file mode 100644 index 9af123827..000000000 --- a/pkg/lang/frontend/starlark/v0/data/const.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2022 The envd 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 data - -const ( - ruleEnvdManagedDataSource = "data.envd" - huggingFaceDatasetPath = "~/.cache/huggingface" - dglFaceDatasetPath = "~/.dgl" -) diff --git a/pkg/lang/frontend/starlark/v0/data/rule.go b/pkg/lang/frontend/starlark/v0/data/rule.go deleted file mode 100644 index 44c804b29..000000000 --- a/pkg/lang/frontend/starlark/v0/data/rule.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2022 The envd 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 data - -import ( - "github.com/sirupsen/logrus" - "go.starlark.net/starlark" - "go.starlark.net/starlarkstruct" - - envddata "github.com/tensorchord/envd/pkg/data" -) - -var ( - logger = logrus.WithField("frontend", "starlark") -) - -var Module = &starlarkstruct.Module{ - Name: "data", - Members: starlark.StringDict{ - "envd": starlark.NewBuiltin(ruleEnvdManagedDataSource, ruleValueEnvdManagedDataSource), - "path": &starlarkstruct.Module{ - Name: "path", - Members: starlark.StringDict{ - "huggingface": starlark.String(huggingFaceDatasetPath), - "dgl": starlark.String(dglFaceDatasetPath), - }}, - }, -} - -func ruleValueEnvdManagedDataSource(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var name starlark.String - - if err := starlark.UnpackArgs(ruleEnvdManagedDataSource, args, kwargs, - "name?", &name); err != nil { - return nil, err - } - logger.Debugf("rule `%s` is invoked, name=%s", - ruleEnvdManagedDataSource, name) - - return NewDataSourceValue(envddata.NewEnvdManagedDataSource(name.GoString())), nil -} diff --git a/pkg/lang/frontend/starlark/v0/data/util.go b/pkg/lang/frontend/starlark/v0/data/util.go deleted file mode 100644 index 8f8692c73..000000000 --- a/pkg/lang/frontend/starlark/v0/data/util.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2022 The envd 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 data - -import ( - "go.starlark.net/starlark" - - envddata "github.com/tensorchord/envd/pkg/data" -) - -type DataSourceValue struct { - source envddata.DataSource -} - -func (d DataSourceValue) Init() error { - return d.source.Init() -} - -func (d DataSourceValue) GetHostDir() (string, error) { - return d.source.GetHostDir() -} - -func (d DataSourceValue) String() string { - return d.source.Type() -} - -func (d DataSourceValue) Type() string { - return d.source.Type() -} - -func (d DataSourceValue) Freeze() {} - -func (d DataSourceValue) Truth() starlark.Bool { return true } - -func (d DataSourceValue) Hash() (uint32, error) { - return d.source.Hash() -} - -func NewDataSourceValue(source envddata.DataSource) *DataSourceValue { - return &DataSourceValue{source: source} -} diff --git a/pkg/lang/frontend/starlark/v0/install/const.go b/pkg/lang/frontend/starlark/v0/install/const.go deleted file mode 100644 index 3f39a8ff8..000000000 --- a/pkg/lang/frontend/starlark/v0/install/const.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2022 The envd 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 install - -const ( - ruleSystemPackage = "install.apt_packages" - rulePyPIPackage = "install.python_packages" - ruleRPackage = "install.r_packages" - ruleCUDA = "install.cuda" - ruleVSCode = "install.vscode_extensions" - ruleConda = "install.conda_packages" - ruleJulia = "install.julia_packages" -) diff --git a/pkg/lang/frontend/starlark/v0/install/install.go b/pkg/lang/frontend/starlark/v0/install/install.go deleted file mode 100644 index feea5f48a..000000000 --- a/pkg/lang/frontend/starlark/v0/install/install.go +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright 2022 The envd 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 install - -import ( - "github.com/cockroachdb/errors" - "github.com/sirupsen/logrus" - "go.starlark.net/starlark" - "go.starlark.net/starlarkstruct" - - ir "github.com/tensorchord/envd/pkg/lang/ir/v0" - "github.com/tensorchord/envd/pkg/util/starlarkutil" -) - -var ( - logger = logrus.WithField("frontend", "starlark") -) - -var Module = &starlarkstruct.Module{ - Name: "install", - Members: starlark.StringDict{ - "python_packages": starlark.NewBuiltin(rulePyPIPackage, ruleFuncPyPIPackage), - "r_packages": starlark.NewBuiltin(ruleRPackage, ruleFuncRPackage), - "apt_packages": starlark.NewBuiltin(ruleSystemPackage, ruleFuncSystemPackage), - "cuda": starlark.NewBuiltin(ruleCUDA, ruleFuncCUDA), - "vscode_extensions": starlark.NewBuiltin(ruleVSCode, ruleFuncVSCode), - "conda_packages": starlark.NewBuiltin(ruleConda, ruleFuncConda), - "julia_packages": starlark.NewBuiltin(ruleJulia, ruleFuncJulia), - }, -} - -func ruleFuncPyPIPackage(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var name *starlark.List - var requirementsFile starlark.String - var wheels *starlark.List - - if err := starlark.UnpackArgs(rulePyPIPackage, args, kwargs, - "name?", &name, "requirements?", &requirementsFile, "local_wheels?", &wheels); err != nil { - return nil, err - } - - nameList, err := starlarkutil.ToStringSlice(name) - if err != nil { - return nil, err - } - - requirementsFileStr := requirementsFile.GoString() - - localWheels, err := starlarkutil.ToStringSlice(wheels) - if err != nil { - return nil, err - } - - logger.Debugf("rule `%s` is invoked, name=%v, requirements=%s, local_wheels=%s", - rulePyPIPackage, nameList, requirementsFileStr, localWheels) - - err = ir.PyPIPackage(nameList, requirementsFileStr, localWheels) - return starlark.None, err -} - -func ruleFuncRPackage(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var name *starlark.List - - if err := starlark.UnpackArgs(ruleRPackage, - args, kwargs, "name", &name); err != nil { - return nil, err - } - - nameList, err := starlarkutil.ToStringSlice(name) - if err != nil { - return nil, err - } - - logger.Debugf("rule `%s` is invoked, name=%v", ruleRPackage, nameList) - ir.RPackage(nameList) - - return starlark.None, nil -} - -func ruleFuncJulia(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var name *starlark.List - - if err := starlark.UnpackArgs(ruleJulia, - args, kwargs, "name", &name); err != nil { - return nil, err - } - - nameList, err := starlarkutil.ToStringSlice(name) - if err != nil { - return nil, err - } - logger.Debugf("rule `%s` is invoked, name=%v", ruleJulia, nameList) - ir.JuliaPackage(nameList) - - return starlark.None, nil -} - -func ruleFuncSystemPackage(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var name *starlark.List - - if err := starlark.UnpackArgs(ruleSystemPackage, - args, kwargs, "name?", &name); err != nil { - return nil, err - } - - nameList, err := starlarkutil.ToStringSlice(name) - if err != nil { - return nil, err - } - - logger.Debugf("rule `%s` is invoked, name=%v", ruleSystemPackage, nameList) - ir.SystemPackage(nameList) - - return starlark.None, nil -} - -func ruleFuncCUDA(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var version, cudnn string - - if err := starlark.UnpackArgs(ruleCUDA, args, kwargs, - "version", &version, "cudnn?", &cudnn); err != nil { - return nil, err - } - - logger.Debugf("rule `%s` is invoked, version=%s, cudnn=%s", - ruleCUDA, version, cudnn) - ir.CUDA(version, cudnn) - - return starlark.None, nil -} - -func ruleFuncVSCode(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var plugins *starlark.List - - if err := starlark.UnpackArgs(ruleVSCode, - args, kwargs, "name", &plugins); err != nil { - return nil, err - } - - pluginList, err := starlarkutil.ToStringSlice(plugins) - if err != nil { - return nil, err - } - - logger.Debugf("rule `%s` is invoked, plugins=%v", ruleVSCode, pluginList) - if err := ir.VSCodePlugins(pluginList); err != nil { - return starlark.None, err - } - - return starlark.None, nil -} - -func ruleFuncConda(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var name, channel *starlark.List - var envFile starlark.String - - if err := starlark.UnpackArgs(ruleConda, - args, kwargs, "name?", &name, "channel?", &channel, "env_file?", &envFile); err != nil { - return nil, err - } - - nameList, err := starlarkutil.ToStringSlice(name) - if err != nil { - return nil, err - } - - channelList, err := starlarkutil.ToStringSlice(channel) - if err != nil { - return nil, err - } - - envFileStr := envFile.GoString() - if envFileStr != "" { - if (len(nameList) != 0) || (len(channelList) != 0) { - return nil, errors.New("env_file and name/channel are mutually exclusive") - } - } - - logger.Debugf("rule `%s` is invoked, name=%v, channel=%v, env_file=%s", ruleConda, nameList, channelList, envFileStr) - if err := ir.CondaPackage(nameList, channelList, envFileStr); err != nil { - return starlark.None, err - } - - return starlark.None, nil -} diff --git a/pkg/lang/frontend/starlark/v0/interpreter.go b/pkg/lang/frontend/starlark/v0/interpreter.go deleted file mode 100644 index b4fbbf457..000000000 --- a/pkg/lang/frontend/starlark/v0/interpreter.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2022 The envd 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 v0 - -import ( - "bytes" - "hash/fnv" - "io/fs" - "os" - "path/filepath" - "strconv" - "strings" - - "github.com/cockroachdb/errors" - "github.com/sirupsen/logrus" - "go.starlark.net/starlark" - - interp "github.com/tensorchord/envd/pkg/lang/frontend/starlark" - "github.com/tensorchord/envd/pkg/lang/frontend/starlark/v0/config" - "github.com/tensorchord/envd/pkg/lang/frontend/starlark/v0/data" - "github.com/tensorchord/envd/pkg/lang/frontend/starlark/v0/install" - "github.com/tensorchord/envd/pkg/lang/frontend/starlark/v0/io" - "github.com/tensorchord/envd/pkg/lang/frontend/starlark/v0/runtime" - "github.com/tensorchord/envd/pkg/lang/frontend/starlark/v0/universe" - "github.com/tensorchord/envd/pkg/util/fileutil" -) - -type entry struct { - globals starlark.StringDict - err error -} - -// generalInterpreter is the interpreter implementation for Starlark. -// Please refer to https://github.com/google/starlark-go -type generalInterpreter struct { - predeclared starlark.StringDict - buildContextDir string - cache map[string]*entry -} - -func NewInterpreter(buildContextDir string) interp.Interpreter { - // Register envd rules and built-in variables to Starlark. - universe.RegisterEnvdRules() - universe.RegisterBuildContext(buildContextDir) - - return &generalInterpreter{ - predeclared: starlark.StringDict{ - "install": install.Module, - "config": config.Module, - "io": io.Module, - "runtime": runtime.Module, - "data": data.Module, - }, - buildContextDir: buildContextDir, - cache: make(map[string]*entry), - } -} - -func (s *generalInterpreter) NewThread(module string) *starlark.Thread { - thread := &starlark.Thread{ - Name: module, - Load: s.load, - } - return thread -} - -func (s *generalInterpreter) load(thread *starlark.Thread, module string) (starlark.StringDict, error) { - return s.exec(thread, module) -} - -func (s *generalInterpreter) exec(thread *starlark.Thread, module string) (starlark.StringDict, error) { - // There are two cases: - // 1. module exists - // 2. there's an explicit `nil` placeholder for module in s.cache - // 3. module does not exist in s.cache - e, ok := s.cache[module] - - // Case 1. - if e != nil { - return e.globals, e.err - } - - // Case 2. - // There is an explicit `nil` for module, which means we are in the middle of exec module. - if ok { - return nil, errors.Newf("Detected cycle import during parsing %s", module) - } - - // Case 3. - // Add a placeholder to indicate "load in progress". - s.cache[module] = nil - - if !strings.HasPrefix(module, universe.GitPrefix) { - var data interface{} - globals, err := starlark.ExecFile(thread, module, data, s.predeclared) - e = &entry{globals, err} - } else { - // exec remote git repo - url := module[len(universe.GitPrefix):] - path, err := fileutil.DownloadOrUpdateGitRepo(url) - if err != nil { - return nil, err - } - globals, err := s.loadGitModule(thread, path) - e = &entry{globals, err} - } - - // Update the cache. - s.cache[module] = e - - return e.globals, e.err -} - -func (s *generalInterpreter) loadGitModule(thread *starlark.Thread, path string) (globals starlark.StringDict, err error) { - var src interface{} - globals = starlark.StringDict{} - logger := logrus.WithField("file", thread.Name) - logger.Debugf("load git module from: %s", path) - err = filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error { - if err != nil { - return err - } - if d.IsDir() || !strings.HasSuffix(d.Name(), ".envd") { - return nil - } - dict, err := starlark.ExecFile(thread, path, src, s.predeclared) - if err != nil { - return err - } - for key, val := range dict { - if _, exist := globals[key]; exist { - return errors.Newf("found duplicated object name: %s in %s", key, path) - } - if !strings.HasPrefix(key, "_") { - globals[key] = val - } - } - return nil - }) - return -} - -func (s generalInterpreter) ExecFile(filename string, funcname string) (interface{}, error) { - logrus.WithField("filename", filename).Debug("interprete the file") - thread := s.NewThread(filename) - globals, err := s.exec(thread, filename) - if err != nil { - return nil, err - } - if funcname != "" { - logrus.Debugf("Execute %s func", funcname) - if globals.Has(funcname) { - buildVar := globals[funcname] - if fn, ok := buildVar.(*starlark.Function); ok { - _, err := starlark.Call(thread, fn, nil, nil) - if err != nil { - return nil, errors.Wrapf(err, "Exception when exec %s func", funcname) - } - } else { - return nil, errors.Errorf("%s is not a function", funcname) - } - } else { - return nil, errors.Errorf("envd file doesn't has %s function", funcname) - } - - } - return globals, nil -} - -func (s generalInterpreter) Eval(script string) (interface{}, error) { - thread := s.NewThread(script) - return starlark.ExecFile(thread, "", script, s.predeclared) -} - -func GetEnvdProgramHash(filename string) (string, error) { - envdSrc, err := os.ReadFile(filename) - if err != nil { - return "", err - } - // No Check builtin or predeclared for now - funcAlwaysHas := func(x string) bool { - return true - } - _, prog, err := starlark.SourceProgram(filename, envdSrc, funcAlwaysHas) - if err != nil { - return "", err - } - buf := new(bytes.Buffer) - err = prog.Write(buf) - if err != nil { - return "", err - } - h := fnv.New64a() - h.Write(buf.Bytes()) - hashsum := h.Sum64() - return strconv.FormatUint(hashsum, 16), nil -} diff --git a/pkg/lang/frontend/starlark/v0/interpreter_test.go b/pkg/lang/frontend/starlark/v0/interpreter_test.go deleted file mode 100644 index edcd71af8..000000000 --- a/pkg/lang/frontend/starlark/v0/interpreter_test.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2022 The envd 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 v0 - -import ( - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -var _ = Describe("Starlark", func() { - It("should be able to compile envd", func() { - filename := "testdata/test.envd" - hash, err := GetEnvdProgramHash(filename) - Expect(err).NotTo(HaveOccurred()) - Expect(hash).To(Equal("cff1c81818116d42")) - }) -}) diff --git a/pkg/lang/frontend/starlark/v0/io/io.go b/pkg/lang/frontend/starlark/v0/io/io.go deleted file mode 100644 index 0367a1a23..000000000 --- a/pkg/lang/frontend/starlark/v0/io/io.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2022 The envd 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 io - -import ( - "github.com/sirupsen/logrus" - "go.starlark.net/starlark" - "go.starlark.net/starlarkstruct" - - ir "github.com/tensorchord/envd/pkg/lang/ir/v0" -) - -var ( - logger = logrus.WithField("frontend", "starlark") -) - -var Module = &starlarkstruct.Module{ - Name: "io", - Members: starlark.StringDict{ - "copy": starlark.NewBuiltin(ruleCopy, ruleFuncCopy), - "http": starlark.NewBuiltin(ruleHTTP, ruleFuncHTTP), - }, -} - -func ruleFuncCopy(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var source, destination starlark.String - - if err := starlark.UnpackArgs(ruleCopy, args, kwargs, - "host_path?", &source, "envd_path?", &destination); err != nil { - return nil, err - } - - sourceStr := source.GoString() - destinationStr := destination.GoString() - - logger.Debugf("rule `%s` is invoked, src=%s, dest=%s\n", - ruleCopy, sourceStr, destinationStr) - ir.Copy(sourceStr, destinationStr) - - return starlark.None, nil -} - -func ruleFuncHTTP(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var url, checksum, filename string - if err := starlark.UnpackArgs(ruleHTTP, args, kwargs, - "url", &url, "checksum?", &checksum, "filename?", &filename); err != nil { - return nil, err - } - - logger.Debugf("rule `%s` is invoked, ruleHTTP, url=%s, checksum=%s, filename=%s\n", - ruleHTTP, url, checksum, filename) - if err := ir.HTTP(url, checksum, filename); err != nil { - return nil, err - } - return starlark.None, nil -} diff --git a/pkg/lang/frontend/starlark/v0/runtime/const.go b/pkg/lang/frontend/starlark/v0/runtime/const.go deleted file mode 100644 index 584e4c069..000000000 --- a/pkg/lang/frontend/starlark/v0/runtime/const.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2022 The envd 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 runtime - -const ( - ruleCommand = "runtime.command" - ruleExpose = "runtime.expose" - ruleDaemon = "runtime.daemon" - ruleEnviron = "runtime.environ" - ruleMount = "runtime.mount" - ruleInitScript = "runtime.init" -) diff --git a/pkg/lang/frontend/starlark/v0/runtime/runtime.go b/pkg/lang/frontend/starlark/v0/runtime/runtime.go deleted file mode 100644 index b3b26b4ee..000000000 --- a/pkg/lang/frontend/starlark/v0/runtime/runtime.go +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright 2022 The envd 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 runtime - -import ( - "net" - "os/user" - "path/filepath" - "strings" - - "github.com/cockroachdb/errors" - "github.com/sirupsen/logrus" - "go.starlark.net/starlark" - "go.starlark.net/starlarkstruct" - - "github.com/tensorchord/envd/pkg/lang/frontend/starlark/v0/data" - ir "github.com/tensorchord/envd/pkg/lang/ir/v0" - "github.com/tensorchord/envd/pkg/util/fileutil" - "github.com/tensorchord/envd/pkg/util/starlarkutil" -) - -var ( - logger = logrus.WithField("frontend", "starlark") -) - -var Module = &starlarkstruct.Module{ - Name: "runtime", - Members: starlark.StringDict{ - "command": starlark.NewBuiltin(ruleCommand, ruleFuncCommand), - "daemon": starlark.NewBuiltin(ruleDaemon, ruleFuncDaemon), - "expose": starlark.NewBuiltin(ruleExpose, ruleFuncExpose), - "environ": starlark.NewBuiltin(ruleEnviron, ruleFuncEnviron), - "mount": starlark.NewBuiltin(ruleMount, ruleFuncMount), - "init": starlark.NewBuiltin(ruleInitScript, ruleFuncInitScript), - }, -} - -func ruleFuncCommand(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var commands starlark.IterableMapping - - if err := starlark.UnpackArgs(ruleCommand, args, kwargs, - "commands?", &commands); err != nil { - return nil, err - } - - commandsMap := make(map[string]string) - for _, tuple := range commands.Items() { - if len(tuple) != 2 { - return nil, errors.Newf("invalid command in %s", ruleCommand) - } - - commandsMap[tuple[0].(starlark.String).GoString()] = - tuple[1].(starlark.String).GoString() - } - - logger.Debugf("rule `%s` is invoked, commands: %v", - ruleCommand, commandsMap) - - ir.RuntimeCommands(commandsMap) - return starlark.None, nil -} - -func ruleFuncDaemon(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var commands *starlark.List - - if err := starlark.UnpackArgs(ruleDaemon, args, kwargs, "commands", &commands); err != nil { - return nil, err - } - - commandList := [][]string{} - if commands != nil { - for i := 0; i < commands.Len(); i++ { - args, ok := commands.Index(i).(*starlark.List) - if !ok { - return nil, errors.Newf("invalid daemon commands (%s)", commands.Index(i).String()) - } - argList := []string{} - for j := 0; j < args.Len(); j++ { - argList = append(argList, args.Index(j).(starlark.String).GoString()) - } - commandList = append(commandList, argList) - } - - logger.Debugf("rule `%s` is invoked, commands=%v", ruleDaemon, commandList) - ir.RuntimeDaemon(commandList) - } - return starlark.None, nil -} - -func ruleFuncExpose(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var ( - envdPort starlark.Int - hostPort = starlark.MakeInt(0) // 0 means envd can randomly choose a free port - serviceName = starlark.String("") - listeningAddr = starlark.String("127.0.0.1") // default to lisen only on local loopback interface - ) - - if err := starlark.UnpackArgs(ruleExpose, - args, kwargs, "envd_port", &envdPort, "host_port?", &hostPort, "service?", &serviceName, "listen_addr?", &listeningAddr); err != nil { - return nil, err - } - envdPortInt, ok := envdPort.Int64() - if !ok || envdPortInt < 1 || envdPortInt > 65535 { - return nil, errors.New("envd_port must be a positive integer less than 65535") - } - hostPortInt, ok := hostPort.Int64() - if !ok || hostPortInt < 0 || hostPortInt > 65535 { - return nil, errors.New("host_port must be a positive integer less than 65535") - } - serviceNameStr := serviceName.GoString() - listeningAddrStr := listeningAddr.GoString() - if net.ParseIP(listeningAddrStr) == nil { - return nil, errors.New("listening_addr must be a valid IP address") - } - - logger.Debugf("rule `%s` is invoked, envd_port=%d, host_port=%d, service=%s", ruleExpose, envdPortInt, hostPortInt, serviceNameStr) - err := ir.RuntimeExpose(int(envdPortInt), int(hostPortInt), serviceNameStr, listeningAddrStr) - return starlark.None, err -} - -func ruleFuncEnviron(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var env starlark.IterableMapping - var path *starlark.List - - if err := starlark.UnpackArgs(ruleCommand, args, kwargs, - "env?", &env, "extra_path?", &path); err != nil { - return nil, err - } - - envMap := make(map[string]string) - if env != nil { - for _, tuple := range env.Items() { - if len(tuple) != 2 { - return nil, errors.Newf("invalid env (%s)", tuple.String()) - } - envMap[tuple[0].(starlark.String).GoString()] = tuple[1].(starlark.String).GoString() - } - } - - pathList, err := starlarkutil.ToStringSlice(path) - if err != nil { - return nil, err - } - - logger.Debugf("rule `%s` is invoked, env: %v, extra_path: %v", ruleEnviron, envMap, pathList) - ir.RuntimeEnviron(envMap, pathList) - return starlark.None, nil -} - -func ruleFuncMount(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var source starlark.Value - var destination starlark.String - - if err := starlark.UnpackArgs(ruleMount, args, kwargs, - "host_path?", &source, "envd_path?", &destination); err != nil { - return nil, err - } - - var sourceStr string - var err error - - if v, ok := source.(*data.DataSourceValue); ok { - err = v.Init() - if err != nil { - return starlark.None, err - } - sourceStr, err = v.GetHostDir() - if err != nil { - return starlark.None, err - } - } else if vs, ok := source.(starlark.String); ok { - sourceStr = vs.GoString() - } else { - return starlark.None, errors.New("invalid data source") - } - - destinationStr := destination.GoString() - - logger.Debugf("rule `%s` is invoked, src=%s, dest=%s", - ruleMount, sourceStr, destinationStr) - - // Expand source directory based on host user - usr, _ := user.Current() - dir := usr.HomeDir - if sourceStr == "~" { - sourceStr = dir - } else if strings.HasPrefix(sourceStr, "~/") { - sourceStr = filepath.Join(dir, sourceStr[2:]) - } - // Expand dest directory based on container user envd - dir = fileutil.EnvdHomeDir() - if destinationStr == "~" { - destinationStr = dir - } else if strings.HasPrefix(destinationStr, "~/") { - destinationStr = fileutil.EnvdHomeDir(destinationStr[2:]) - } - ir.Mount(sourceStr, destinationStr) - - return starlark.None, nil -} - -func ruleFuncInitScript(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var commands *starlark.List - - if err := starlark.UnpackArgs(ruleCommand, args, kwargs, - "commands?", &commands); err != nil { - return nil, err - } - - commandsSlice, err := starlarkutil.ToStringSlice(commands) - if err != nil { - return nil, err - } - - logger.Debugf("rule `%s` is invoked, commands: %v", - ruleInitScript, commandsSlice) - - ir.RuntimeInitScript(commandsSlice) - return starlark.None, nil -} diff --git a/pkg/lang/frontend/starlark/v0/testdata/test.envd b/pkg/lang/frontend/starlark/v0/testdata/test.envd deleted file mode 100644 index 9c0e3cb02..000000000 --- a/pkg/lang/frontend/starlark/v0/testdata/test.envd +++ /dev/null @@ -1,8 +0,0 @@ -def build(): - base(os="ubuntu20.04", language="python3") - install.python_packages( - name=[ - "numpy", - ] - ) - shell("zsh") diff --git a/pkg/lang/frontend/starlark/v0/universe/universe.go b/pkg/lang/frontend/starlark/v0/universe/universe.go deleted file mode 100644 index 2b990327b..000000000 --- a/pkg/lang/frontend/starlark/v0/universe/universe.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2022 The envd 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 universe - -import ( - "fmt" - - "github.com/sirupsen/logrus" - "go.starlark.net/starlark" - "go.starlark.net/starlarkstruct" - - "github.com/tensorchord/envd/pkg/lang/frontend/starlark/v0/builtin" - ir "github.com/tensorchord/envd/pkg/lang/ir/v0" - "github.com/tensorchord/envd/pkg/util/starlarkutil" -) - -var ( - logger = logrus.WithField("frontend", "starlark") -) - -// RegisterEnvdRules registers built-in envd rules into the global namespace. -func RegisterEnvdRules() { - starlark.Universe[ruleBase] = starlark.NewBuiltin(ruleBase, ruleFuncBase) - starlark.Universe[ruleShell] = starlark.NewBuiltin(ruleShell, ruleFuncShell) - starlark.Universe[ruleRun] = starlark.NewBuiltin(ruleRun, ruleFuncRun) - starlark.Universe[ruleGitConfig] = starlark.NewBuiltin(ruleGitConfig, ruleFuncGitConfig) - starlark.Universe[ruleInclude] = starlark.NewBuiltin(ruleInclude, ruleFuncInclude) -} - -func RegisterBuildContext(buildContextDir string) { - starlark.Universe[builtin.BuildContextDir] = starlark.String(buildContextDir) -} - -func ruleFuncBase(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var os, language, image string - - if err := starlark.UnpackArgs(ruleBase, args, kwargs, - "os?", &os, "language?", &language, "image?", &image); err != nil { - return nil, err - } - - logger.Debugf("rule `%s` is invoked, os=%s, language=%s, image=%s\n", - ruleBase, os, language, image) - - err := ir.Base(os, language, image) - return starlark.None, err -} - -func ruleFuncRun(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var commands *starlark.List - mountHost := false - - if err := starlark.UnpackArgs(ruleRun, - args, kwargs, "commands", &commands, "mount_host?", &mountHost); err != nil { - return nil, err - } - - goCommands, err := starlarkutil.ToStringSlice(commands) - if err != nil { - return nil, err - } - - logger.Debugf("rule `%s` is invoked, commands=%v, mount_host=%t", ruleRun, goCommands, mountHost) - if err := ir.Run(goCommands, mountHost); err != nil { - return nil, err - } - - return starlark.None, nil -} - -func ruleFuncShell(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var shell starlark.String - - if err := starlark.UnpackPositionalArgs(ruleShell, args, kwargs, 1, &shell); err != nil { - return nil, err - } - - shellStr := shell.GoString() - - logger.Debugf("rule `%s` is invoked, shell=%s", ruleShell, shellStr) - - err := ir.Shell(shellStr) - return starlark.None, err -} - -func ruleFuncGitConfig(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var name, email, editor starlark.String - - if err := starlark.UnpackArgs(ruleGitConfig, - args, kwargs, "name?", &name, "email?", &email, "editor?", &editor); err != nil { - return nil, err - } - - nameStr := name.GoString() - emailStr := email.GoString() - editorStr := editor.GoString() - - logger.Debugf("rule `%s` is invoked, name=%s, email=%s, editor=%s", - ruleGitConfig, nameStr, emailStr, editorStr) - - err := ir.Git(nameStr, emailStr, editorStr) - return starlark.None, err -} - -func ruleFuncInclude(thread *starlark.Thread, _ *starlark.Builtin, - args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var gitRepo string - - if err := starlark.UnpackArgs(ruleInclude, - args, kwargs, "git?", &gitRepo); err != nil { - return nil, err - } - - logger.Debugf("rule `%s` is invoked, git=%s", ruleInclude, gitRepo) - - globals, err := thread.Load(thread, fmt.Sprintf("%s%s", GitPrefix, gitRepo)) - if err != nil { - return nil, err - } - module := &starlarkstruct.Module{ - Name: gitRepo, - Members: globals, - } - return module, nil -} diff --git a/pkg/lang/frontend/starlark/v1/builtin/builtin.go b/pkg/lang/frontend/starlark/v1/builtin/builtin.go index 148ba725c..1b46b99cb 100644 --- a/pkg/lang/frontend/starlark/v1/builtin/builtin.go +++ b/pkg/lang/frontend/starlark/v1/builtin/builtin.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/lang/frontend/starlark/v1/config/config.go b/pkg/lang/frontend/starlark/v1/config/config.go index a2ac698f0..01e0f41cc 100644 --- a/pkg/lang/frontend/starlark/v1/config/config.go +++ b/pkg/lang/frontend/starlark/v1/config/config.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -46,9 +46,29 @@ var Module = &starlarkstruct.Module{ "entrypoint": starlark.NewBuiltin(ruleEntrypoint, ruleFuncEntrypoint), "repo": starlark.NewBuiltin(ruleRepo, ruleFuncRepo), "owner": starlark.NewBuiltin(ruleOwner, ruleFuncOwner), + "shm_size": starlark.NewBuiltin(ruleShmSize, ruleFuncShmSize), }, } +func ruleFuncShmSize(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { + var shmSize starlark.Int + + if err := starlark.UnpackArgs(ruleShmSize, args, kwargs, "size", &shmSize); err != nil { + return nil, err + } + + shmSizeInt, ok := shmSize.Int64() + + if ok { + ir.ShmSize(int(shmSizeInt)) + logger.Debugf("Using %d shm size", int(shmSizeInt)) + } else { + logger.Debugf("Failed to convert shm size to int64") + } + + return starlark.None, nil +} + func ruleFuncGPU(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { var numGPUs starlark.Int diff --git a/pkg/lang/frontend/starlark/v1/config/const.go b/pkg/lang/frontend/starlark/v1/config/const.go index d7a93e064..be7576922 100644 --- a/pkg/lang/frontend/starlark/v1/config/const.go +++ b/pkg/lang/frontend/starlark/v1/config/const.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -26,4 +26,5 @@ const ( ruleEntrypoint = "config.entrypoint" ruleRepo = "config.repo" ruleOwner = "config.owner" + ruleShmSize = "config.shm_size" ) diff --git a/pkg/lang/frontend/starlark/v1/data/const.go b/pkg/lang/frontend/starlark/v1/data/const.go index 9af123827..6b6e6441d 100644 --- a/pkg/lang/frontend/starlark/v1/data/const.go +++ b/pkg/lang/frontend/starlark/v1/data/const.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/lang/frontend/starlark/v1/data/rule.go b/pkg/lang/frontend/starlark/v1/data/rule.go index 44c804b29..cd69b61bf 100644 --- a/pkg/lang/frontend/starlark/v1/data/rule.go +++ b/pkg/lang/frontend/starlark/v1/data/rule.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/lang/frontend/starlark/v1/data/util.go b/pkg/lang/frontend/starlark/v1/data/util.go index 8f8692c73..89d84f812 100644 --- a/pkg/lang/frontend/starlark/v1/data/util.go +++ b/pkg/lang/frontend/starlark/v1/data/util.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/lang/frontend/starlark/v1/install/const.go b/pkg/lang/frontend/starlark/v1/install/const.go index 2f79810f1..83d21b55c 100644 --- a/pkg/lang/frontend/starlark/v1/install/const.go +++ b/pkg/lang/frontend/starlark/v1/install/const.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,6 +18,8 @@ const ( // language rulePython = "install.python" ruleConda = "install.conda" + ruleUV = "install.uv" + rulePixi = "install.pixi" ruleRLang = "install.r_lang" ruleJulia = "install.julia" diff --git a/pkg/lang/frontend/starlark/v1/install/install.go b/pkg/lang/frontend/starlark/v1/install/install.go index badd6a7ad..2a33b46aa 100644 --- a/pkg/lang/frontend/starlark/v1/install/install.go +++ b/pkg/lang/frontend/starlark/v1/install/install.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -34,6 +34,8 @@ var Module = &starlarkstruct.Module{ // language "python": starlark.NewBuiltin(rulePython, ruleFuncPython), "conda": starlark.NewBuiltin(ruleConda, ruleFuncConda), + "uv": starlark.NewBuiltin(ruleUV, ruleFuncUV), + "pixi": starlark.NewBuiltin(rulePixi, ruleFuncPixi), "r_lang": starlark.NewBuiltin(ruleRLang, ruleFuncRLang), "julia": starlark.NewBuiltin(ruleJulia, ruleFuncJulia), // packages @@ -77,6 +79,35 @@ func ruleFuncConda(thread *starlark.Thread, _ *starlark.Builtin, return starlark.None, nil } +func ruleFuncPixi(thread *starlark.Thread, _ *starlark.Builtin, + args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { + + usePixiMirror := false + var pypiIndex string + + if err := starlark.UnpackArgs(rulePixi, args, kwargs, "use_pixi_mirror?", &usePixiMirror, "pypi_index?", &pypiIndex); err != nil { + return nil, err + } + + logger.Debugf("rule `%s` is invoked: use_pixi_mirror=%t, pypi_index=%v", rulePixi, usePixiMirror, pypiIndex) + ir.Pixi(usePixiMirror, pypiIndex) + return starlark.None, nil +} + +func ruleFuncUV(thread *starlark.Thread, _ *starlark.Builtin, + args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { + logger.Debugf("rule `%s` is invoked", ruleUV) + + pythonVersion := ir.PythonVersionDefault + + if err := starlark.UnpackArgs(ruleUV, args, kwargs, "python_version?", &pythonVersion); err != nil { + return nil, err + } + + ir.UV(pythonVersion) + return starlark.None, nil +} + func ruleFuncRLang(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { logger.Debugf("rule `%s` is invoked", ruleRLang) diff --git a/pkg/lang/frontend/starlark/v1/interpreter.go b/pkg/lang/frontend/starlark/v1/interpreter.go index ca403edd8..e814800fb 100644 --- a/pkg/lang/frontend/starlark/v1/interpreter.go +++ b/pkg/lang/frontend/starlark/v1/interpreter.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ import ( "github.com/cockroachdb/errors" "github.com/sirupsen/logrus" "go.starlark.net/starlark" + "go.starlark.net/syntax" interp "github.com/tensorchord/envd/pkg/lang/frontend/starlark" "github.com/tensorchord/envd/pkg/lang/frontend/starlark/v1/config" @@ -42,6 +43,19 @@ type entry struct { err error } +func envdStarlarkResolveOptions() *syntax.FileOptions { + return &syntax.FileOptions{ + // resolver + Set: false, + While: false, + TopLevelControl: false, + GlobalReassign: false, + LoadBindsGlobally: false, + // compiler + Recursion: true, + } +} + // generalInterpreter is the interpreter implementation for Starlark. // Please refer to https://github.com/google/starlark-go type generalInterpreter struct { @@ -104,7 +118,7 @@ func (s *generalInterpreter) exec(thread *starlark.Thread, module string) (starl if !strings.HasPrefix(module, universe.GitPrefix) { var data interface{} - globals, err := starlark.ExecFile(thread, module, data, s.predeclared) + globals, err := starlark.ExecFileOptions(envdStarlarkResolveOptions(), thread, module, data, s.predeclared) e = &entry{globals, err} } else { // exec remote git repo @@ -135,7 +149,7 @@ func (s *generalInterpreter) loadGitModule(thread *starlark.Thread, path string) if d.IsDir() || !strings.HasSuffix(d.Name(), ".envd") { return nil } - dict, err := starlark.ExecFile(thread, path, src, s.predeclared) + dict, err := starlark.ExecFileOptions(envdStarlarkResolveOptions(), thread, path, src, s.predeclared) if err != nil { return err } @@ -153,7 +167,7 @@ func (s *generalInterpreter) loadGitModule(thread *starlark.Thread, path string) } func (s generalInterpreter) ExecFile(filename string, funcname string) (interface{}, error) { - logrus.WithField("filename", filename).Debug("interprete the file") + logrus.WithField("filename", filename).Debug("interpret the file") thread := s.NewThread(filename) globals, err := s.exec(thread, filename) if err != nil { @@ -181,7 +195,7 @@ func (s generalInterpreter) ExecFile(filename string, funcname string) (interfac func (s generalInterpreter) Eval(script string) (interface{}, error) { thread := s.NewThread(script) - return starlark.ExecFile(thread, "", script, s.predeclared) + return starlark.ExecFileOptions(envdStarlarkResolveOptions(), thread, "", script, s.predeclared) } func GetEnvdProgramHash(filename string) (string, error) { @@ -193,7 +207,7 @@ func GetEnvdProgramHash(filename string) (string, error) { funcAlwaysHas := func(x string) bool { return true } - _, prog, err := starlark.SourceProgram(filename, envdSrc, funcAlwaysHas) + _, prog, err := starlark.SourceProgramOptions(envdStarlarkResolveOptions(), filename, envdSrc, funcAlwaysHas) if err != nil { return "", err } diff --git a/pkg/lang/frontend/starlark/v1/interpreter_test.go b/pkg/lang/frontend/starlark/v1/interpreter_test.go index 08a921b87..7ba8a25ab 100644 --- a/pkg/lang/frontend/starlark/v1/interpreter_test.go +++ b/pkg/lang/frontend/starlark/v1/interpreter_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -24,6 +24,6 @@ var _ = Describe("Starlark", func() { filename := "testdata/test.envd" hash, err := GetEnvdProgramHash(filename) Expect(err).NotTo(HaveOccurred()) - Expect(hash).To(Equal("1292b11c2ac16f70")) + Expect(hash).To(Equal("42bc7dd445dbc5a0")) }) }) diff --git a/pkg/lang/frontend/starlark/v1/io/const.go b/pkg/lang/frontend/starlark/v1/io/const.go index d46769e9e..391ff50fe 100644 --- a/pkg/lang/frontend/starlark/v1/io/const.go +++ b/pkg/lang/frontend/starlark/v1/io/const.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/lang/frontend/starlark/v1/io/io.go b/pkg/lang/frontend/starlark/v1/io/io.go index 71cb9e1df..5cc50af75 100644 --- a/pkg/lang/frontend/starlark/v1/io/io.go +++ b/pkg/lang/frontend/starlark/v1/io/io.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -36,19 +36,16 @@ var Module = &starlarkstruct.Module{ func ruleFuncCopy(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var source, destination starlark.String + var source, destination, image string if err := starlark.UnpackArgs(ruleCopy, args, kwargs, - "host_path?", &source, "envd_path?", &destination); err != nil { + "source", &source, "target", &destination, "image?", &image); err != nil { return nil, err } - sourceStr := source.GoString() - destinationStr := destination.GoString() - - logger.Debugf("rule `%s` is invoked, src=%s, dest=%s\n", - ruleCopy, sourceStr, destinationStr) - ir.Copy(sourceStr, destinationStr) + logger.Debugf("rule `%s` is invoked, src=%s, dest=%s, image=%s\n", + ruleCopy, source, destination, image) + ir.Copy(source, destination, image) return starlark.None, nil } diff --git a/pkg/lang/frontend/starlark/v1/runtime/const.go b/pkg/lang/frontend/starlark/v1/runtime/const.go index 584e4c069..5143851c7 100644 --- a/pkg/lang/frontend/starlark/v1/runtime/const.go +++ b/pkg/lang/frontend/starlark/v1/runtime/const.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/lang/frontend/starlark/v1/runtime/runtime.go b/pkg/lang/frontend/starlark/v1/runtime/runtime.go index 29f220857..449d6cfd4 100644 --- a/pkg/lang/frontend/starlark/v1/runtime/runtime.go +++ b/pkg/lang/frontend/starlark/v1/runtime/runtime.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/lang/frontend/starlark/v1/starlark_suite_test.go b/pkg/lang/frontend/starlark/v1/starlark_suite_test.go index 3fbdd42a5..fc370760b 100644 --- a/pkg/lang/frontend/starlark/v1/starlark_suite_test.go +++ b/pkg/lang/frontend/starlark/v1/starlark_suite_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/lang/frontend/starlark/v1/universe/const.go b/pkg/lang/frontend/starlark/v1/universe/const.go index 7e2b74e68..4528bce4f 100644 --- a/pkg/lang/frontend/starlark/v1/universe/const.go +++ b/pkg/lang/frontend/starlark/v1/universe/const.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/lang/frontend/starlark/v1/universe/universe.go b/pkg/lang/frontend/starlark/v1/universe/universe.go index a03eb079e..09cc63155 100644 --- a/pkg/lang/frontend/starlark/v1/universe/universe.go +++ b/pkg/lang/frontend/starlark/v1/universe/universe.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/lang/ir/graph.go b/pkg/lang/ir/graph.go index 15d4587ac..be0404ae2 100644 --- a/pkg/lang/ir/graph.go +++ b/pkg/lang/ir/graph.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,15 +18,21 @@ import ( "context" "github.com/moby/buildkit/client/llb" + specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/tensorchord/envd/pkg/progress/compileui" ) type Graph interface { - Compile(ctx context.Context, envName string, pub string) (*llb.Definition, error) + Compile(ctx context.Context, envPath string, pub string, platform *specs.Platform, progressMode string) (*llb.Definition, error) graphDebugger graphVisitor + graphSerializer +} + +type graphSerializer interface { + GeneralGraphFromLabel(label []byte) (Graph, error) } type graphDebugger interface { @@ -37,6 +43,8 @@ type graphVisitor interface { GetDepsFiles(deps []string) []string GPUEnabled() bool GetNumGPUs() int + GetShmSize() int + IsDev() bool Labels() (map[string]string, error) ExposedPorts() (map[string]struct{}, error) GetEntrypoint(buildContextDir string) ([]string, error) @@ -51,4 +59,6 @@ type graphVisitor interface { GetHTTP() []HTTPInfo GetRuntimeCommands() map[string]string GetUser() string + GetPlatform() *specs.Platform + GetWorkingDir() string } diff --git a/pkg/lang/ir/types.go b/pkg/lang/ir/types.go index 02bcbda5e..7a0fab8f7 100644 --- a/pkg/lang/ir/types.go +++ b/pkg/lang/ir/types.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -31,6 +31,7 @@ type RuntimeGraph struct { type CopyInfo struct { Source string Destination string + Image string } type MountInfo struct { @@ -60,6 +61,15 @@ type CondaConfig struct { UseMicroMamba bool } +type UVConfig struct { + PythonVersion string +} + +type PixiConfig struct { + UsePixiMirror bool + PyPIIndex *string +} + type GitConfig struct { Name string Email string diff --git a/pkg/lang/ir/util.go b/pkg/lang/ir/util.go index 204b093b3..c079ec2ec 100644 --- a/pkg/lang/ir/util.go +++ b/pkg/lang/ir/util.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,11 +15,47 @@ package ir import ( + "context" "encoding/json" + "fmt" "github.com/cockroachdb/errors" + "github.com/containers/image/v5/docker" + "github.com/containers/image/v5/image" + "github.com/containers/image/v5/types" + specs "github.com/opencontainers/image-spec/specs-go/v1" ) +func FetchImageConfig(ctx context.Context, imageName string, platform *specs.Platform) (config specs.ImageConfig, err error) { + ref, err := docker.ParseReference(fmt.Sprintf("//%s", imageName)) + if err != nil { + return config, errors.Wrap(err, "failed to parse image reference") + } + sys := types.SystemContext{} + if platform != nil { + sys.ArchitectureChoice = platform.Architecture + sys.OSChoice = platform.OS + } + src, err := ref.NewImageSource(ctx, &sys) + if err != nil { + return config, errors.Wrap(err, "failed to get image source from ref") + } + defer src.Close() + digest, err := docker.GetDigest(ctx, &sys, ref) + if err != nil { + return config, errors.Wrap(err, "failed to get the image digest") + } + image, err := image.FromUnparsedImage(ctx, &sys, image.UnparsedInstance(src, &digest)) + if err != nil { + return config, errors.Wrap(err, "failed to get unparsed image") + } + img, err := image.OCIConfig(ctx) + if err != nil { + return config, errors.Wrap(err, "failed to get OCI config") + } + return img.Config, nil +} + func (rg *RuntimeGraph) Dump() (string, error) { b, err := json.Marshal(rg) if err != nil { diff --git a/pkg/lang/ir/v0/cache.go b/pkg/lang/ir/v0/cache.go deleted file mode 100644 index 4354183f5..000000000 --- a/pkg/lang/ir/v0/cache.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2022 The envd 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 v0 - -import ( - "fmt" - - "github.com/sirupsen/logrus" -) - -func (g generalGraph) CacheID(filename string) string { - var cacheID string - if g.CUDA != nil { - cacheID = fmt.Sprintf("%s-gpu", filename) - } else { - cacheID = fmt.Sprintf("%s-cpu", filename) - } - logrus.Debugf("%s calculated cacheID: %s", filename, cacheID) - return cacheID -} diff --git a/pkg/lang/ir/v0/checker.go b/pkg/lang/ir/v0/checker.go deleted file mode 100644 index ba1d7bbe5..000000000 --- a/pkg/lang/ir/v0/checker.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2022 The envd 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 v0 - -import ( - "reflect" - "regexp" - "strings" -) - -func (g generalGraph) GetDepsFiles(deps []string) []string { - tHandle := reflect.TypeOf(g) - vHandle := reflect.ValueOf(g) - deps = searchFileInGraph(tHandle, vHandle, deps) - return deps -} - -// Match all filed in ir.Graph with the given keyword -func likeFileFiled(str string) bool { - nameKeyword := []string{ - "File", - "Path", - "Wheels", - } - if len(nameKeyword) == 0 { - return true - } - re := regexp.MustCompile(strings.Join(nameKeyword, "|")) - return re.MatchString(str) -} - -// search all files in Graph -func searchFileInGraph(tHandle reflect.Type, vHandle reflect.Value, deps []string) []string { - for i := 0; i < vHandle.NumField(); i++ { - v := vHandle.Field(i) - if v.Type().Kind() == reflect.Struct { - t := v.Type() - deps = searchFileInGraph(t, v, deps) - } else if v.Type().Kind() == reflect.Ptr { - if v.Type().Elem().Kind() == reflect.Struct { - if v.Elem().CanAddr() { - t := v.Type().Elem() - deps = searchFileInGraph(t, v.Elem(), deps) - } - } - } else { - t := tHandle.Field(i) - fieldName := t.Name - if likeFileFiled(fieldName) { - typeName := t.Type.String() - if v.Interface() != nil { - if typeName == "string" { - deps = append(deps, v.Interface().(string)) - } - if typeName == "*string" { - deps = append(deps, *(v.Interface().(*string))) - } - if typeName == "[]string" { - filesList := v.Interface().([]string) - deps = append(deps, filesList...) - } - } - } - } - } - return deps -} diff --git a/pkg/lang/ir/v0/compile.go b/pkg/lang/ir/v0/compile.go deleted file mode 100644 index fdea4af3f..000000000 --- a/pkg/lang/ir/v0/compile.go +++ /dev/null @@ -1,345 +0,0 @@ -// Copyright 2022 The envd 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 v0 - -import ( - "context" - "encoding/json" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/cockroachdb/errors" - "github.com/moby/buildkit/client/llb" - "github.com/sirupsen/logrus" - "github.com/spf13/viper" - servertypes "github.com/tensorchord/envd-server/api/types" - - "github.com/tensorchord/envd/pkg/config" - "github.com/tensorchord/envd/pkg/flag" - "github.com/tensorchord/envd/pkg/lang/ir" - "github.com/tensorchord/envd/pkg/progress/compileui" - "github.com/tensorchord/envd/pkg/types" - "github.com/tensorchord/envd/pkg/util/fileutil" - "github.com/tensorchord/envd/pkg/version" -) - -func NewGraph() ir.Graph { - runtimeGraph := ir.RuntimeGraph{ - RuntimeCommands: make(map[string]string), - RuntimeEnviron: make(map[string]string), - RuntimeEnvPaths: []string{types.DefaultSystemPath}, - } - langVersion := languageVersionDefault - conda := &ir.CondaConfig{} - return &generalGraph{ - OS: osDefault, - Language: ir.Language{ - Name: languageDefault, - Version: &langVersion, - }, - CUDA: nil, - CUDNN: CUDNNVersionDefault, - NumGPUs: 0, - - PyPIPackages: [][]string{}, - RPackages: []string{}, - JuliaPackages: []string{}, - SystemPackages: []string{}, - Exec: []ir.RunBuildCommand{}, - UserDirectories: []string{}, - Shell: shellBASH, - CondaConfig: conda, - RuntimeGraph: runtimeGraph, - } -} - -var DefaultGraph = NewGraph() - -func (g generalGraph) GetNumGPUs() int { - return g.NumGPUs -} - -func (g generalGraph) GetShell() string { - return g.Shell -} - -func (g generalGraph) GetMount() []ir.MountInfo { - return g.Mount -} - -func (g generalGraph) GetEnvironmentName() string { - return g.EnvironmentName -} - -func (g generalGraph) GetJupyterConfig() *ir.JupyterConfig { - return g.JupyterConfig -} - -func (g generalGraph) GetRStudioServerConfig() *ir.RStudioServerConfig { - return g.RStudioServerConfig -} - -func (g generalGraph) GetExposedPorts() []ir.ExposeItem { - return g.RuntimeExpose -} - -func (g generalGraph) GetRuntimeCommands() map[string]string { - return g.RuntimeCommands -} - -func (g generalGraph) GetUser() string { - return "envd" -} - -func (g *generalGraph) Compile(ctx context.Context, envName string, pub string) (*llb.Definition, error) { - w, err := compileui.New(ctx, os.Stdout, "auto") - if err != nil { - return nil, errors.Wrap(err, "failed to create compileui") - } - g.Writer = w - g.EnvironmentName = envName - g.PublicKeyPath = pub - - uid, gid, err := getUIDGID() - if err != nil { - return nil, errors.Wrap(err, "failed to get uid/gid") - } - state, err := g.CompileLLB(uid, gid) - if err != nil { - return nil, errors.Wrap(err, "failed to compile the graph") - } - // TODO(gaocegege): Support multi platform. - def, err := state.Marshal(ctx, llb.LinuxAmd64) - if err != nil { - return nil, errors.Wrap(err, "failed to marshal the llb definition") - } - return def, nil -} - -func (g generalGraph) GPUEnabled() bool { - return g.CUDA != nil -} - -func (g generalGraph) Labels() (map[string]string, error) { - labels := make(map[string]string) - str, err := json.Marshal(g.SystemPackages) - if err != nil { - return nil, err - } - labels[types.ImageLabelAPT] = string(str) - pyPackages := []string{} - for _, pkg := range g.PyPIPackages { - pyPackages = append(pyPackages, pkg...) - } - str, err = json.Marshal(pyPackages) - if err != nil { - return nil, err - } - labels[types.ImageLabelPyPI] = string(str) - str, err = json.Marshal(g.RPackages) - if err != nil { - return nil, err - } - labels[types.ImageLabelR] = string(str) - if g.GPUEnabled() { - labels[types.ImageLabelGPU] = "true" - labels[types.ImageLabelCUDA] = *g.CUDA - labels[types.ImageLabelCUDNN] = g.CUDNN - } - labels[types.ImageLabelVendor] = types.ImageVendorEnvd - code, err := g.RuntimeGraph.Dump() - if err != nil { - return labels, err - } - labels[types.RuntimeGraphCode] = code - - ports := []servertypes.EnvironmentPort{} - ports = append(ports, servertypes.EnvironmentPort{ - Name: "ssh", - Port: config.SSHPortInContainer, - }) - if g.JupyterConfig != nil { - ports = append(ports, servertypes.EnvironmentPort{ - Name: "jupyter", - Port: config.JupyterPortInContainer, - }) - } - if g.RStudioServerConfig != nil { - ports = append(ports, servertypes.EnvironmentPort{ - Name: "rstudio-server", - Port: config.RStudioServerPortInContainer, - }) - } - - if g.RuntimeExpose != nil && len(g.RuntimeExpose) > 0 { - for _, item := range g.RuntimeExpose { - ports = append(ports, servertypes.EnvironmentPort{ - Name: item.ServiceName, - Port: int32(item.EnvdPort), - }) - } - } - - portsData, err := json.Marshal(ports) - if err != nil { - return labels, err - } - labels[types.ImageLabelPorts] = string(portsData) - - repoInfo, err := json.Marshal(g.Repo) - if err != nil { - return labels, err - } - labels[types.ImageLabelRepo] = string(repoInfo) - - labels[types.ImageLabelContainerName] = string(g.EnvironmentName) - return labels, nil -} - -func (g generalGraph) ExposedPorts() (map[string]struct{}, error) { - ports := make(map[string]struct{}) - - // do not expose ports for custom images - if g.Image != nil { - return ports, nil - } - - ports[fmt.Sprintf("%d/tcp", config.SSHPortInContainer)] = struct{}{} - if g.JupyterConfig != nil { - ports[fmt.Sprintf("%d/tcp", config.JupyterPortInContainer)] = struct{}{} - } - if g.RStudioServerConfig != nil { - ports[fmt.Sprintf("%d/tcp", config.RStudioServerPortInContainer)] = struct{}{} - } - - if g.RuntimeExpose != nil && len(g.RuntimeExpose) > 0 { - for _, item := range g.RuntimeExpose { - ports[fmt.Sprintf("%d/tcp", item.EnvdPort)] = struct{}{} - } - } - - return ports, nil -} - -func (g generalGraph) EnvString() []string { - var envs []string - for k, v := range g.RuntimeEnviron { - envs = append(envs, fmt.Sprintf("%s=%s", k, v)) - } - envs = append(envs, fmt.Sprintf("PATH=%s", strings.Join(g.RuntimeEnvPaths, ":"))) - return envs -} - -func (g generalGraph) GetEnviron() []string { - if g.Image != nil { - return g.EnvString() - } - return append(g.EnvString(), - "LC_ALL=en_US.UTF-8", - "LANG=en_US.UTF-8", - ) -} - -func (g *generalGraph) SetWriter(w compileui.Writer) { - g.Writer = w -} - -func (g generalGraph) GetHTTP() []ir.HTTPInfo { - return g.HTTP -} - -func (g generalGraph) DefaultCacheImporter() (*string, error) { - // The base remote cache should work for all languages. - var res string - if g.CUDA != nil { - res = fmt.Sprintf( - "type=registry,ref=docker.io/%s/python-cache:envd-%s-cuda-%s-cudnn-%s", - viper.GetString(flag.FlagDockerOrganization), - version.GetVersionForImageTag(), *g.CUDA, g.CUDNN) - } else { - res = fmt.Sprintf( - "type=registry,ref=docker.io/%s/python-cache:envd-%s", - viper.GetString(flag.FlagDockerOrganization), - version.GetVersionForImageTag()) - } - return &res, nil -} - -func (g *generalGraph) GetEntrypoint(buildContextDir string) ([]string, error) { - if g.Image != nil { - return g.Entrypoint, nil - } - g.RuntimeEnviron[types.EnvdWorkDir] = fileutil.EnvdHomeDir(filepath.Base(buildContextDir)) - return []string{"horust"}, nil -} - -func (g *generalGraph) CompileLLB(uid, gid int) (llb.State, error) { - g.uid = uid - - // TODO(gaocegege): Remove the hack for https://github.com/tensorchord/envd/issues/370 - g.gid = 1001 - logrus.WithFields(logrus.Fields{ - "uid": g.uid, - "gid": g.gid, - }).Debug("compile LLB") - - // TODO(gaocegege): Support more OS and langs. - aptStage, err := g.compileBase() - if err != nil { - return llb.State{}, errors.Wrap(err, "failed to get the base image") - } - var merged llb.State - // Use custom logic when image is specified. - if g.Image != nil { - merged, err = g.compileCustomPython(aptStage) - if err != nil { - return llb.State{}, errors.Wrap(err, "failed to compile custom python image") - } - } else { - switch g.Language.Name { - case "r": - merged, err = g.compileRLang(aptStage) - if err != nil { - return llb.State{}, errors.Wrap(err, "failed to compile r language") - } - case "python": - merged, err = g.compilePython(aptStage) - if err != nil { - return llb.State{}, errors.Wrap(err, "failed to compile python") - } - case "julia": - merged, err = g.compileJulia(aptStage) - if err != nil { - return llb.State{}, errors.Wrap(err, "failed to compile julia") - } - } - } - - prompt := g.compilePrompt(merged) - copy := g.compileCopy(prompt) - // TODO(gaocegege): Support order-based exec. - run := g.compileRun(copy) - git := g.compileGit(run) - user := g.compileUserOwn(git) - mount := g.compileMountDir(user) - entrypoint, err := g.compileEntrypoint(mount) - if err != nil { - return llb.State{}, errors.Wrap(err, "failed to compile entrypoint") - } - g.Writer.Finish() - return entrypoint, nil -} diff --git a/pkg/lang/ir/v0/conda.go b/pkg/lang/ir/v0/conda.go deleted file mode 100644 index cabe7e194..000000000 --- a/pkg/lang/ir/v0/conda.go +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2022 The envd 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 v0 - -import ( - _ "embed" - "fmt" - "path/filepath" - "strings" - - "github.com/cockroachdb/errors" - "github.com/moby/buildkit/client/llb" - "github.com/sirupsen/logrus" - - "github.com/tensorchord/envd/pkg/flag" - "github.com/tensorchord/envd/pkg/types" -) - -const ( - condaVersionDefault = "py39_4.11.0" - // check the issue https://github.com/mamba-org/mamba/issues/1975 - mambaVersionDefault = "0.25.1" - condaRootPrefix = "/opt/conda" - condaBinDir = "/opt/conda/bin" -) - -var ( - // this file can be used by both conda and mamba - // https://mamba.readthedocs.io/en/latest/user_guide/configuration.html#multiple-rc-files - condarc = "/opt/conda/.condarc" - //go:embed install-conda.sh - installCondaBash string - //go:embed install-mamba.sh - installMambaBash string -) - -func (g generalGraph) compileCondaChannel(root llb.State) llb.State { - if g.CondaConfig.CondaChannel != nil { - logrus.WithField("conda-channel", *g.CondaChannel).Debug("using custom conda channel") - stage := root. - File(llb.Mkfile(condarc, - 0644, []byte(*g.CondaChannel), llb.WithUIDGID(g.uid, g.gid)), llb.WithCustomName("[internal] setting conda channel")) - return stage - } - return root -} - -func (g generalGraph) condaCommandPath() string { - if g.CondaConfig.UseMicroMamba { - return filepath.Join(condaBinDir, "micromamba") - } - return filepath.Join(condaBinDir, "conda") -} - -func (g generalGraph) condaInitShell(shell string) string { - path := g.condaCommandPath() - if g.CondaConfig.UseMicroMamba { - return fmt.Sprintf("%s shell init -p %s -s %s", path, condaRootPrefix, shell) - } - return fmt.Sprintf("%s init %s", path, shell) -} - -func (g generalGraph) condaUpdateFromFile() string { - args := fmt.Sprintf("update -n envd --file %s", g.CondaEnvFileName) - if g.CondaConfig.UseMicroMamba { - return fmt.Sprintf("%s %s", g.condaCommandPath(), args) - } - return fmt.Sprintf("%s env %s", g.condaCommandPath(), args) -} - -func (g *generalGraph) compileCondaPackages(root llb.State) llb.State { - if len(g.CondaConfig.CondaPackages) == 0 && len(g.CondaEnvFileName) == 0 { - logrus.Debug("Conda packages not enabled") - return root - } - - cacheDir := filepath.Join(condaRootPrefix, "pkgs") - // Refer to https://github.com/moby/buildkit/blob/31054718bf775bf32d1376fe1f3611985f837584/frontend/dockerfile/dockerfile2llb/convert_runmount.go#L46 - cacheMount := llb.Scratch().File(llb.Mkdir("/cache-conda", 0755, llb.WithParents(true)), - llb.WithCustomName("[internal] setting conda cache mount permissions")) - - // Compose the package install command. - var sb strings.Builder - var run llb.ExecState - - if len(g.CondaEnvFileName) > 0 { - sb.WriteString(g.condaUpdateFromFile()) - } else { - if len(g.CondaConfig.AdditionalChannels) == 0 { - sb.WriteString(fmt.Sprintf("%s install -n envd", g.condaCommandPath())) - } else { - sb.WriteString(fmt.Sprintf("%s install -n envd", g.condaCommandPath())) - for _, channel := range g.CondaConfig.AdditionalChannels { - sb.WriteString(fmt.Sprintf(" -c %s", channel)) - } - } - for _, pkg := range g.CondaConfig.CondaPackages { - sb.WriteString(fmt.Sprintf(" %s", pkg)) - } - } - - cmd := sb.String() - run = root.Dir(g.getWorkingDir()). - Run(llb.Shlex(cmd), llb.WithCustomNamef("[internal] %s %s", - cmd, strings.Join(g.CondaPackages, " "))) - run.AddMount(g.getWorkingDir(), llb.Local(flag.FlagBuildContext)) - run.AddMount(cacheDir, cacheMount, - llb.AsPersistentCacheDir(g.CacheID(cacheDir), llb.CacheMountShared), llb.SourcePath("/cache-conda")) - return run.Root() -} - -func (g generalGraph) compileCondaEnvironment(root llb.State) (llb.State, error) { - // Always init bash since we will use it to create jupyter notebook service. - run := root.Run( - llb.Shlexf(`bash -c "%s"`, g.condaInitShell("bash")), - llb.WithCustomName("[internal] initialize conda bash environment"), - ) - - pythonVersion, err := g.getAppropriatePythonVersion() - if err != nil { - return llb.State{}, errors.Wrap(err, "failed to get python version") - } - // Create a conda environment. - cmd := fmt.Sprintf("bash -c \"%s create -n envd python=%s\"", g.condaCommandPath(), pythonVersion) - run = run.Run(llb.Shlex(cmd), - llb.WithCustomNamef("[internal] create conda environment: %s", cmd)) - - return run.Root(), nil -} - -func (g *generalGraph) installConda(root llb.State) llb.State { - root = g.updateEnvPath(root, types.DefaultCondaPath) - // this directory is related to conda envd env meta (used by `conda env config vars set key=value`) - g.UserDirectories = append(g.UserDirectories, fmt.Sprintf("%s/envs/envd/conda-meta", condaRootPrefix)) - if g.CondaConfig.UseMicroMamba { - run := root.AddEnv("MAMBA_BIN_DIR", condaBinDir). - AddEnv("MAMBA_ROOT_PREFIX", condaRootPrefix). - AddEnv("MAMBA_VERSION", mambaVersionDefault). - Run(llb.Shlexf("bash -c '%s'", installMambaBash), - llb.WithCustomName("[internal] install micro mamba")) - return run.Root() - } - run := root.AddEnv("CONDA_VERSION", condaVersionDefault). - File(llb.Mkdir(condaRootPrefix, 0755, llb.WithParents(true)), - llb.WithCustomName("[internal] create conda directory")). - Run(llb.Shlexf("bash -c '%s'", installCondaBash), - llb.WithCustomName("[internal] install conda")) - return run.Root() -} diff --git a/pkg/lang/ir/v0/consts.go b/pkg/lang/ir/v0/consts.go deleted file mode 100644 index 6e11f21ca..000000000 --- a/pkg/lang/ir/v0/consts.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2022 The envd 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 v0 - -import "github.com/tensorchord/envd/pkg/util/fileutil" - -const ( - osDefault = "ubuntu20.04" - languageDefault = "python" - languageVersionDefault = "3" - CUDNNVersionDefault = "8" - - aptSourceFilePath = "/etc/apt/sources.list" - pypiIndexFilePath = "/etc/pip.conf" - - pypiConfigTemplate = ` -[global] -index-url=%s -%s - -[install] -src = /tmp -` -) - -var ( - // used inside the container - defaultConfigDir = fileutil.EnvdHomeDir(".config") - starshipConfigPath = fileutil.EnvdHomeDir(".config", "starship.toml") -) diff --git a/pkg/lang/ir/v0/custom.go b/pkg/lang/ir/v0/custom.go deleted file mode 100644 index 7daa40847..000000000 --- a/pkg/lang/ir/v0/custom.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2022 The envd 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 v0 - -import ( - "fmt" - "strings" - - "github.com/moby/buildkit/client/llb" -) - -// nolint:unparam -func (g generalGraph) compileCustomPython(baseStage llb.State) (llb.State, error) { - aptStage := g.compileUbuntuAPT(baseStage) - pypiMirrorStage := g.compilePyPIIndex(aptStage) - systemStage := g.compileCustomSystemPackages(pypiMirrorStage) - pypiStage := g.compileCustomPyPIPackages(systemStage) - - return pypiStage, nil -} - -func (g generalGraph) compileCustomPyPIPackages(root llb.State) llb.State { - if len(g.PyPIPackages) == 0 { - return root - } - - cacheDir := "/home/root/.cache" - // Refer to https://github.com/moby/buildkit/blob/31054718bf775bf32d1376fe1f3611985f837584/frontend/dockerfile/dockerfile2llb/convert_runmount.go#L46 - cache := llb.Scratch().File(llb.Mkdir("/cache", 0755, llb.WithParents(true)), - llb.WithCustomName("[internal] settings pip cache mount permissions")) - - for _, packages := range g.PyPIPackages { - cmd := fmt.Sprintf("pip install %s", strings.Join(packages, " ")) - run := root. - Run(llb.Shlex(cmd), llb.WithCustomNamef("pip install %s", - strings.Join(packages, " "))) - run.AddMount(cacheDir, cache, - llb.AsPersistentCacheDir(g.CacheID(cacheDir), llb.CacheMountShared), llb.SourcePath("/cache")) - root = run.Root() - } - return root -} - -func (g generalGraph) compileCustomSystemPackages(root llb.State) llb.State { - if len(g.SystemPackages) == 0 { - return root - } - - // Compose the package install command. - var sb strings.Builder - sb.WriteString("apt-get update && apt-get install -y --no-install-recommends --no-install-suggests --fix-missing") - - for _, pkg := range g.SystemPackages { - sb.WriteString(fmt.Sprintf(" %s", pkg)) - } - - cacheDir := "/var/cache/apt" - cacheLibDir := "/var/lib/apt" - - run := root.Run(llb.Shlexf(`bash -c "%s"`, sb.String()), - llb.WithCustomNamef("apt-get install %s", - strings.Join(g.SystemPackages, " "))) - run.AddMount(cacheDir, llb.Scratch(), - llb.AsPersistentCacheDir(g.CacheID(cacheDir), llb.CacheMountShared)) - run.AddMount(cacheLibDir, llb.Scratch(), - llb.AsPersistentCacheDir(g.CacheID(cacheLibDir), llb.CacheMountShared)) - return run.Root() -} diff --git a/pkg/lang/ir/v0/editor.go b/pkg/lang/ir/v0/editor.go deleted file mode 100644 index 4d7bd68ad..000000000 --- a/pkg/lang/ir/v0/editor.go +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2022 The envd 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 v0 - -import ( - "fmt" - "strconv" - - "github.com/cockroachdb/errors" - "github.com/moby/buildkit/client/llb" - - "github.com/tensorchord/envd/pkg/config" - "github.com/tensorchord/envd/pkg/editor/vscode" - "github.com/tensorchord/envd/pkg/flag" - "github.com/tensorchord/envd/pkg/progress/compileui" - "github.com/tensorchord/envd/pkg/types" - "github.com/tensorchord/envd/pkg/util/fileutil" -) - -func (g generalGraph) compileVSCode() (*llb.State, error) { - if len(g.VSCodePlugins) == 0 { - return nil, nil - } - inputs := []llb.State{} - for _, p := range g.VSCodePlugins { - vscodeClient, err := vscode.NewClient(vscode.MarketplaceVendorOpenVSX) - if err != nil { - return nil, errors.Wrap(err, "failed to create vscode client") - } - g.Writer.LogVSCodePlugin(p, compileui.ActionStart, false) - if cached, err := vscodeClient.DownloadOrCache(p); err != nil { - return nil, err - } else { - g.Writer.LogVSCodePlugin(p, compileui.ActionEnd, cached) - } - ext := llb.Scratch().File(llb.Copy(llb.Local(flag.FlagCacheDir), - vscodeClient.PluginPath(p), - fileutil.EnvdHomeDir(".vscode-server", "extensions", p.String()), - &llb.CopyInfo{ - CreateDestPath: true, - }, llb.WithUIDGID(g.uid, g.gid)), - llb.WithCustomNamef("install vscode plugin %s", p.String())) - inputs = append(inputs, ext) - } - layer := llb.Merge(inputs, llb.WithCustomName("merging plugins for vscode")) - return &layer, nil -} - -func (g *generalGraph) compileJupyter() error { - if g.JupyterConfig == nil { - return nil - } - - g.PyPIPackages = append(g.PyPIPackages, []string{"jupyter"}) - switch g.Language.Name { - case "python": - return nil - default: - return errors.Newf("Jupyter is not supported in %s yet", g.Language.Name) - } -} - -func (g generalGraph) generateJupyterCommand(workingDir string) []string { - if g.JupyterConfig == nil { - return nil - } - - if g.JupyterConfig.Token == "" { - g.JupyterConfig.Token = "''" - } - - // get from env if not set - if len(workingDir) == 0 { - workingDir = fmt.Sprintf("${%s}", types.EnvdWorkDir) - } - - cmd := []string{ - "python3", "-m", "notebook", - "--ip", "0.0.0.0", "--notebook-dir", workingDir, - "--NotebookApp.token", g.JupyterConfig.Token, - "--port", strconv.Itoa(config.JupyterPortInContainer), - } - - if g.uid == 0 { - cmd = append(cmd, "--allow-root") - } - - return cmd -} - -// nolint:unparam -func (g generalGraph) generateRStudioCommand(workingDir string) []string { - if g.RStudioServerConfig == nil { - return nil - } - - // get from env if not set - // if len(workingDir) == 0 { - // workingDir = fmt.Sprintf("${%s}", types.EnvdWorkDir) - // } - - return []string{ - // TODO(gaocegege): Remove root permission here. - "sudo", - "/usr/lib/rstudio-server/bin/rserver", - // TODO(gaocegege): Support working dir. - } -} diff --git a/pkg/lang/ir/v0/editor_test.go b/pkg/lang/ir/v0/editor_test.go deleted file mode 100644 index ca5d62e50..000000000 --- a/pkg/lang/ir/v0/editor_test.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2022 The envd 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 v0 - -import ( - "testing" - - "github.com/tensorchord/envd/pkg/lang/ir" -) - -func TestGenerateCommand(t *testing.T) { - testcases := []struct { - graph generalGraph - dir string - expected []string - }{ - { - graph: generalGraph{ - JupyterConfig: &ir.JupyterConfig{ - Token: "", - Port: 8888, - }, - uid: 1000, - }, - dir: "test", - expected: []string{ - "python3", "-m", "notebook", "--ip", "0.0.0.0", "--notebook-dir", "test", - "--NotebookApp.token", "''", "--port", "8888", - }, - }, - { - graph: generalGraph{ - JupyterConfig: &ir.JupyterConfig{ - Token: "test", - Port: 8888, - }, - uid: 1000, - }, - dir: "test", - expected: []string{ - "python3", "-m", "notebook", "--ip", "0.0.0.0", "--notebook-dir", "test", - "--NotebookApp.token", "test", "--port", "8888", - }, - }, - { - graph: generalGraph{ - JupyterConfig: &ir.JupyterConfig{ - Token: "test", - Port: 8888, - }, - uid: 0, - }, - dir: "test", - expected: []string{ - "python3", "-m", "notebook", "--ip", "0.0.0.0", "--notebook-dir", "test", - "--NotebookApp.token", "test", "--port", "8888", "--allow-root", - }, - }, - { - graph: generalGraph{}, - dir: "test", - expected: []string{}, - }, - } - for _, tc := range testcases { - actual := tc.graph.generateJupyterCommand(tc.dir) - if !equal(actual, tc.expected) { - t.Errorf("failed to generate the command: expected %v, got %v", tc.expected, actual) - } - } -} - -// Equal tells whether a and b contain the same elements. -// A nil argument is equivalent to an empty slice. -func equal(a, b []string) bool { - if len(a) != len(b) { - return false - } - for i, v := range a { - if v != b[i] { - return false - } - } - return true -} diff --git a/pkg/lang/ir/v0/fs.go b/pkg/lang/ir/v0/fs.go deleted file mode 100644 index f0e4766f6..000000000 --- a/pkg/lang/ir/v0/fs.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2022 The envd 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 v0 - -import ( - "github.com/moby/buildkit/client/llb" -) - -func (g *generalGraph) CompileCacheDir(root llb.State, cacheDir string) llb.State { - g.UserDirectories = append(g.UserDirectories, cacheDir) - run := root.Run(llb.Shlexf("mkdir -p %s", cacheDir), llb.WithCustomName("[internal] create cache dir")) - return run.Root() -} diff --git a/pkg/lang/ir/v0/git.go b/pkg/lang/ir/v0/git.go deleted file mode 100644 index b2352213e..000000000 --- a/pkg/lang/ir/v0/git.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2022 The envd 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 v0 - -import ( - "fmt" - - "github.com/moby/buildkit/client/llb" - - "github.com/tensorchord/envd/pkg/util/fileutil" -) - -const ( - templateGitConfig = ` -[user] - email = %s - name = %s -[core] - editor = %s - -` -) - -func (g *generalGraph) compileGit(root llb.State) llb.State { - if g.GitConfig == nil { - return root - } - content := fmt.Sprintf(templateGitConfig, g.GitConfig.Email, g.GitConfig.Name, g.GitConfig.Editor) - installPath := fileutil.EnvdHomeDir(".gitconfig") - gitStage := root.File(llb.Mkfile(installPath, - 0644, []byte(content), llb.WithUIDGID(g.uid, g.gid))) - return gitStage -} diff --git a/pkg/lang/ir/v0/install-conda.sh b/pkg/lang/ir/v0/install-conda.sh deleted file mode 100644 index 1ee87bb92..000000000 --- a/pkg/lang/ir/v0/install-conda.sh +++ /dev/null @@ -1,27 +0,0 @@ -set -euo pipefail && \ -UNAME_M="$(uname -m)" && \ -if [ "${UNAME_M}" = "x86_64" ]; then \ - MINICONDA_URL="https://repo.anaconda.com/miniconda/Miniconda3-${CONDA_VERSION}-Linux-x86_64.sh"; \ - SHA256SUM="4ee9c3aa53329cd7a63b49877c0babb49b19b7e5af29807b793a76bdb1d362b4"; \ -elif [ "${UNAME_M}" = "s390x" ]; then \ - MINICONDA_URL="https://repo.anaconda.com/miniconda/Miniconda3-${CONDA_VERSION}-Linux-s390x.sh"; \ - SHA256SUM="e5e5e89cdcef9332fe632cd25d318cf71f681eef029a24495c713b18e66a8018"; \ -elif [ "${UNAME_M}" = "aarch64" ]; then \ - MINICONDA_URL="https://repo.anaconda.com/miniconda/Miniconda3-${CONDA_VERSION}-Linux-aarch64.sh"; \ - SHA256SUM="00c7127a8a8d3f4b9c2ab3391c661239d5b9a88eafe895fd0f3f2a8d9c0f4556"; \ -elif [ "${UNAME_M}" = "ppc64le" ]; then \ - MINICONDA_URL="https://repo.anaconda.com/miniconda/Miniconda3-${CONDA_VERSION}-Linux-ppc64le.sh"; \ - SHA256SUM="8ee1f8d17ef7c8cb08a85f7d858b1cb55866c06fcf7545b98c3b82e4d0277e66"; \ -fi && \ -wget "${MINICONDA_URL}" -O /tmp/miniconda.sh && \ -echo "${SHA256SUM} /tmp/miniconda.sh" > /tmp/shasum && \ -if [ "${CONDA_VERSION}" != "latest" ]; then sha256sum --check --status /tmp/shasum; fi && \ -mkdir -p /opt && \ -sh /tmp/miniconda.sh -b -u -p /opt/conda && \ -rm /tmp/miniconda.sh /tmp/shasum && \ -echo ". /opt/conda/etc/profile.d/conda.sh" >> ~/.bashrc && \ -echo "conda activate base" >> ~/.bashrc && \ -echo -e "channels:\n - defaults" > /opt/conda/.condarc && \ -find /opt/conda/ -follow -type f -name '*.a' -delete && \ -find /opt/conda/ -follow -type f -name '*.js.map' -delete && \ -/opt/conda/bin/conda clean -afy diff --git a/pkg/lang/ir/v0/install-mamba.sh b/pkg/lang/ir/v0/install-mamba.sh deleted file mode 100644 index f9610ddfb..000000000 --- a/pkg/lang/ir/v0/install-mamba.sh +++ /dev/null @@ -1,15 +0,0 @@ -set -euo pipefail && \ -ARCH="$(uname -m)" && \ -if [[ "${ARCH}" == "aarch64" ]]; then \ - ARCH="aarch64"; \ -elif [[ "${ARCH}" == "ppc64le" ]]; then \ - ARCH="ppc64le"; \ -else \ - ARCH="64"; \ -fi && \ -mkdir -p ${MAMBA_BIN_DIR} && \ -curl -Ls https://micro.mamba.pm/api/micromamba/linux-${ARCH}/${MAMBA_VERSION} | tar -xvj -C ${MAMBA_BIN_DIR} --strip-components=1 bin/micromamba && \ -chown $(id -u):$(id -g) ${MAMBA_BIN_DIR}/micromamba -ln -s ${MAMBA_BIN_DIR}/micromamba ${MAMBA_BIN_DIR}/conda && \ -echo -e "channels:\n - defaults" > ${MAMBA_ROOT_PREFIX}/.mambarc -echo -e "#!/bin/sh\n\. ${MAMBA_ROOT_PREFIX}/etc/profile.d/micromamba.sh || return \$?\nmicromamba activate \"\$@\"" > ${MAMBA_BIN_DIR}/activate diff --git a/pkg/lang/ir/v0/interface.go b/pkg/lang/ir/v0/interface.go deleted file mode 100644 index 6b3bf5d05..000000000 --- a/pkg/lang/ir/v0/interface.go +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright 2022 The envd 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 v0 - -import ( - "github.com/cockroachdb/errors" - "github.com/opencontainers/go-digest" - - "github.com/tensorchord/envd/pkg/editor/vscode" - "github.com/tensorchord/envd/pkg/lang/ir" - "github.com/tensorchord/envd/pkg/types" -) - -func Base(os, language, image string) error { - l, version, err := parseLanguage(language) - if err != nil { - return err - } - g := DefaultGraph.(*generalGraph) - g.Language = ir.Language{ - Name: l, - Version: version, - } - if len(os) > 0 { - g.OS = os - } - if image != "" { - g.Image = &image - } - return nil -} - -func PyPIPackage(deps []string, requirementsFile string, wheels []string) error { - g := DefaultGraph.(*generalGraph) - - if len(deps) > 0 { - g.PyPIPackages = append(g.PyPIPackages, deps) - } - g.PythonWheels = append(g.PythonWheels, wheels...) - - if requirementsFile != "" { - g.RequirementsFile = &requirementsFile - } - - return nil -} - -func RPackage(deps []string) { - g := DefaultGraph.(*generalGraph) - - g.RPackages = append(g.RPackages, deps...) -} - -func JuliaPackage(deps []string) { - g := DefaultGraph.(*generalGraph) - - g.JuliaPackages = append(g.JuliaPackages, deps...) -} - -func SystemPackage(deps []string) { - g := DefaultGraph.(*generalGraph) - - g.SystemPackages = append(g.SystemPackages, deps...) -} - -func GPU(numGPUs int) { - g := DefaultGraph.(*generalGraph) - - g.NumGPUs = numGPUs -} - -func CUDA(version, cudnn string) { - g := DefaultGraph.(*generalGraph) - - g.CUDA = &version - if len(cudnn) > 0 { - g.CUDNN = cudnn - } -} - -func VSCodePlugins(plugins []string) error { - g := DefaultGraph.(*generalGraph) - - for _, p := range plugins { - plugin, err := vscode.ParsePlugin(p) - if err != nil { - return err - } - g.VSCodePlugins = append(g.VSCodePlugins, *plugin) - } - return nil -} - -// UbuntuAPT updates the Ubuntu apt source.list in the image. -func UbuntuAPT(source string) error { - if source == "" { - return errors.New("source is required") - } - g := DefaultGraph.(*generalGraph) - - g.UbuntuAPTSource = &source - return nil -} - -func PyPIIndex(url, extraURL string, trust bool) error { - if url == "" { - return errors.New("url is required") - } - g := DefaultGraph.(*generalGraph) - - g.PyPIIndexURL = &url - g.PyPIExtraIndexURL = &extraURL - g.PyPITrust = trust - return nil -} - -func CRANMirror(url string) error { - g := DefaultGraph.(*generalGraph) - - g.CRANMirrorURL = &url - return nil -} - -func JuliaPackageServer(url string) error { - g := DefaultGraph.(*generalGraph) - - g.JuliaPackageServer = &url - return nil -} - -func Shell(shell string) error { - g := DefaultGraph.(*generalGraph) - - g.Shell = shell - return nil -} - -func Jupyter(pwd string, port int64) error { - g := DefaultGraph.(*generalGraph) - - g.JupyterConfig = &ir.JupyterConfig{ - Token: pwd, - Port: port, - } - return nil -} - -func RStudioServer() error { - g := DefaultGraph.(*generalGraph) - - g.RStudioServerConfig = &ir.RStudioServerConfig{} - return nil -} - -func Run(commands []string, mount bool) error { - g := DefaultGraph.(*generalGraph) - - g.Exec = append(g.Exec, ir.RunBuildCommand{ - Commands: commands, - MountHost: mount, - }) - return nil -} - -func Git(name, email, editor string) error { - g := DefaultGraph.(*generalGraph) - - g.GitConfig = &ir.GitConfig{ - Name: name, - Email: email, - Editor: editor, - } - return nil -} - -func CondaChannel(channel string, useMamba bool) error { - g := DefaultGraph.(*generalGraph) - - g.CondaConfig.CondaChannel = &channel - g.CondaConfig.UseMicroMamba = useMamba - return nil -} - -func CondaPackage(deps []string, channel []string, envFile string) error { - g := DefaultGraph.(*generalGraph) - - g.CondaConfig.CondaPackages = append( - g.CondaConfig.CondaPackages, deps...) - - g.CondaConfig.CondaEnvFileName = envFile - - if len(channel) != 0 { - g.CondaConfig.AdditionalChannels = append( - g.CondaConfig.AdditionalChannels, channel...) - } - return nil -} - -func Copy(src, dest string) { - g := DefaultGraph.(*generalGraph) - - g.Copy = append(g.Copy, ir.CopyInfo{ - Source: src, - Destination: dest, - }) -} - -func Mount(src, dest string) { - g := DefaultGraph.(*generalGraph) - - g.Mount = append(g.Mount, ir.MountInfo{ - Source: src, - Destination: dest, - }) -} - -func HTTP(url, checksum, filename string) error { - info := ir.HTTPInfo{ - URL: url, - Filename: filename, - } - if len(checksum) > 0 { - d, err := digest.Parse(checksum) - if err != nil { - return err - } - info.Checksum = d - } - g := DefaultGraph.(*generalGraph) - - g.HTTP = append(g.HTTP, info) - return nil -} - -func Entrypoint(args []string) { - g := DefaultGraph.(*generalGraph) - - g.Entrypoint = append(g.Entrypoint, args...) -} - -func RuntimeCommands(commands map[string]string) { - g := DefaultGraph.(*generalGraph) - - for k, v := range commands { - g.RuntimeCommands[k] = v - } -} - -func RuntimeDaemon(commands [][]string) { - g := DefaultGraph.(*generalGraph) - - g.RuntimeDaemon = append(g.RuntimeDaemon, commands...) -} - -func RuntimeExpose(envdPort, hostPort int, serviceName string, listeningAddr string) error { - g := DefaultGraph.(*generalGraph) - - g.RuntimeExpose = append(g.RuntimeExpose, ir.ExposeItem{ - EnvdPort: envdPort, - HostPort: hostPort, - ServiceName: serviceName, - ListeningAddr: listeningAddr, - }) - return nil -} - -func RuntimeEnviron(env map[string]string, path []string) { - g := DefaultGraph.(*generalGraph) - - for k, v := range env { - g.RuntimeEnviron[k] = v - } - g.RuntimeEnvPaths = append(g.RuntimeEnvPaths, path...) -} - -func RuntimeInitScript(commands []string) { - g := DefaultGraph.(*generalGraph) - - g.RuntimeInitScript = append(g.RuntimeInitScript, commands) -} - -func Repo(url, description string) { - g := DefaultGraph.(*generalGraph) - - g.Repo = types.RepoInfo{ - Description: description, - URL: url, - } -} diff --git a/pkg/lang/ir/v0/julia.go b/pkg/lang/ir/v0/julia.go deleted file mode 100644 index 995d42ec7..000000000 --- a/pkg/lang/ir/v0/julia.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2022 The envd 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 v0 - -import ( - "fmt" - "strings" - - "github.com/cockroachdb/errors" - "github.com/moby/buildkit/client/llb" - "github.com/sirupsen/logrus" - - "github.com/tensorchord/envd/pkg/types" -) - -func (g *generalGraph) compileJulia(baseStage llb.State) (llb.State, error) { - baseStage = g.updateEnvPath(baseStage, types.DefaultJuliaPath) - if err := g.compileJupyter(); err != nil { - return llb.State{}, errors.Wrap(err, "failed to compile jupyter") - } - - aptStage := g.compileUbuntuAPT(baseStage) - builtinSystemStage := aptStage - - sshStage, err := g.copySSHKey(builtinSystemStage) - if err != nil { - return llb.State{}, errors.Wrap(err, "failed to copy ssh keys") - } - diffSSHStage := llb.Diff(builtinSystemStage, sshStage, llb.WithCustomName("install ssh keys")) - - shellStage, err := g.compileShell(builtinSystemStage) - if err != nil { - return llb.State{}, errors.Wrap(err, "failed to compile shell") - } - diffShellStage := llb.Diff(builtinSystemStage, shellStage, llb.WithCustomName("install shell")) - - systemStage := llb.Diff(builtinSystemStage, g.compileSystemPackages(builtinSystemStage), - llb.WithCustomName("install system packages")) - - juliaStage := llb.Diff(builtinSystemStage, - g.installJuliaPackages(builtinSystemStage), llb.WithCustomName("install julia packages")) - - vscodeStage, err := g.compileVSCode() - if err != nil { - return llb.State{}, errors.Wrap(err, "failed to get vscode plugins") - } - - var merged llb.State - if vscodeStage != nil { - merged = llb.Merge([]llb.State{ - builtinSystemStage, systemStage, diffShellStage, - diffSSHStage, juliaStage, *vscodeStage, - }, llb.WithCustomName("[internal] generating the image")) - } else { - merged = llb.Merge([]llb.State{ - builtinSystemStage, systemStage, diffShellStage, - diffSSHStage, juliaStage, - }, llb.WithCustomName("[internal] generating the image")) - } - return merged, nil -} - -func (g generalGraph) installJuliaPackages(root llb.State) llb.State { - if len(g.JuliaPackages) == 0 { - return root - } - - var sb strings.Builder - - sb.WriteString(`/usr/local/julia/bin/julia -e 'using Pkg; Pkg.add([`) - for i, pkg := range g.JuliaPackages { - sb.WriteString(fmt.Sprintf(`"%s"`, pkg)) - if i != len(g.JuliaPackages)-1 { - sb.WriteString(", ") - } - } - - sb.WriteString(`])'`) - - // TODO(gaocegege): Support cache. - cmd := sb.String() - logrus.Debug("install julia packages: ", cmd) - root = llb.User("envd")(root) - if g.JuliaPackageServer != nil { - root = root.AddEnv("JULIA_PKG_SERVER", *g.JuliaPackageServer) - } - root = root.AddEnv("PATH", "/usr/local/julia/bin") - run := root. - Run(llb.Shlex(cmd), llb.WithCustomNamef("install julia packages")) - - return run.Root() -} diff --git a/pkg/lang/ir/v0/python.go b/pkg/lang/ir/v0/python.go deleted file mode 100644 index ba31de137..000000000 --- a/pkg/lang/ir/v0/python.go +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright 2022 The envd 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 v0 - -import ( - "fmt" - "net/url" - "path/filepath" - "strings" - - "github.com/cockroachdb/errors" - "github.com/moby/buildkit/client/llb" - "github.com/sirupsen/logrus" - - "github.com/tensorchord/envd/pkg/flag" -) - -const ( - pythonVersionDefault = "3.9" -) - -func (g generalGraph) getAppropriatePythonVersion() (string, error) { - if g.Language.Version == nil { - return pythonVersionDefault, nil - } - - version := *g.Language.Version - if version == "3" || version == "" { - return pythonVersionDefault, nil - } - if strings.HasPrefix(version, "3.") { - return version, nil - } - return "", errors.Errorf("python version %s is not supported", version) -} - -func (g generalGraph) compilePython(baseStage llb.State) (llb.State, error) { - if err := g.compileJupyter(); err != nil { - return llb.State{}, errors.Wrap(err, "failed to compile jupyter") - } - aptStage := g.compileUbuntuAPT(baseStage) - systemStage := g.compileSystemPackages(aptStage) - - condaEnvStage, err := g.compileCondaEnvironment(baseStage) - if err != nil { - return llb.State{}, errors.Wrap(err, "failed to compile conda environment") - } - - // Conda affects shell and python, thus we cannot do it in parallel. - shellStage, err := g.compileShell(baseStage) - if err != nil { - return llb.State{}, errors.Wrap(err, "failed to compile shell") - } - - diffCondaEnvStage := llb.Diff(baseStage, condaEnvStage, - llb.WithCustomName("[internal] conda python environment")) - diffSystemStage := llb.Diff(baseStage, systemStage, - llb.WithCustomName("[internal] install system packages")) - diffShellStage := llb.Diff(baseStage, shellStage, - llb.WithCustomNamef("[internal] configure shell %s", g.Shell)) - prePythonStage := llb.Merge([]llb.State{ - diffSystemStage, - diffCondaEnvStage, - diffShellStage, - baseStage}, llb.WithCustomName("pre-python stage")) - - condaChannelStage := g.compileCondaChannel(prePythonStage) - - condaStage := llb.Diff(prePythonStage, - g.compileCondaPackages(condaChannelStage), - llb.WithCustomName("[internal] install conda packages")) - - pypiMirrorStage := g.compilePyPIIndex(prePythonStage) - - pypiStage := llb.Diff(prePythonStage, - g.compilePyPIPackages(pypiMirrorStage), - llb.WithCustomName("[internal] install PyPI packages")) - - vscodeStage, err := g.compileVSCode() - if err != nil { - return llb.State{}, errors.Wrap(err, "failed to get vscode plugins") - } - sshStage, err := g.copySSHKey(prePythonStage) - if err != nil { - return llb.State{}, errors.Wrap(err, "failed to copy ssh keys") - } - diffSSHStage := llb.Diff(prePythonStage, sshStage, - llb.WithCustomName("[internal] install ssh key")) - - var merged llb.State - if vscodeStage != nil { - merged = llb.Merge([]llb.State{ - prePythonStage, condaStage, pypiStage, - diffSSHStage, *vscodeStage, - }, llb.WithCustomName("[internal] generating the image")) - } else { - merged = llb.Merge([]llb.State{ - prePythonStage, condaStage, - diffSSHStage, pypiStage, - }, llb.WithCustomName("[internal] generating the image")) - } - merged = g.compileAlternative(merged) - condaShell := g.compileCondaShell(merged) - return condaShell, nil -} - -// Set the system default python to envd's python. -func (g generalGraph) compileAlternative(root llb.State) llb.State { - envdPrefix := "/opt/conda/envs/envd/bin" - run := root. - Run(llb.Shlexf("update-alternatives --install /usr/bin/python python %s/python 1", envdPrefix), - llb.WithCustomName("[internal] update alternative python to envd")). - Run(llb.Shlexf("update-alternatives --install /usr/bin/python3 python3 %s/python3 1", envdPrefix), - llb.WithCustomName("[internal] update alternative python3 to envd")). - Run(llb.Shlexf("update-alternatives --install /usr/bin/pip pip %s/pip 1", envdPrefix), - llb.WithCustomName("[internal] update alternative pip to envd")). - Run(llb.Shlexf("update-alternatives --install /usr/bin/pip3 pip3 %s/pip3 1", envdPrefix), - llb.WithCustomName("[internal] update alternative pip3 to envd")) - return run.Root() -} - -func (g generalGraph) compilePyPIPackages(root llb.State) llb.State { - if len(g.PyPIPackages) == 0 && g.RequirementsFile == nil && len(g.PythonWheels) == 0 { - return root - } - - // Create the envd cache directory in the container. see issue #582 - cacheDir := filepath.Join("/", "root", ".cache", "pip") - root = g.CompileCacheDir(root, cacheDir) - - cache := llb.Scratch().File(llb.Mkdir("/cache/pip", 0755, llb.WithParents(true)), - llb.WithCustomName("[internal] setting pip cache mount permissions")) - - if len(g.PyPIPackages) != 0 { - for _, packages := range g.PyPIPackages { - cmd := fmt.Sprintf("/opt/conda/envs/envd/bin/python -m pip install %s", strings.Join(packages, " ")) - logrus.WithField("command", cmd).Debug("Configure pip install statements") - run := root. - Run(llb.Shlex(cmd), llb.WithCustomNamef("pip install %s", strings.Join(packages, " "))) - // Refer to https://github.com/moby/buildkit/blob/31054718bf775bf32d1376fe1f3611985f837584/frontend/dockerfile/dockerfile2llb/convert_runmount.go#L46 - run.AddMount(cacheDir, cache, - llb.AsPersistentCacheDir(g.CacheID(cacheDir), llb.CacheMountShared), llb.SourcePath("/cache/pip")) - root = run.Root() - } - } - - if g.RequirementsFile != nil { - logrus.WithField("file", *g.RequirementsFile). - Debug("Configure pip install requirements statements") - root = root.Dir(g.getWorkingDir()) - run := root. - Run(llb.Shlexf("/opt/conda/envs/envd/bin/python -m pip install -r %s", *g.RequirementsFile), - llb.WithCustomNamef("pip install -r %s", *g.RequirementsFile)) - run.AddMount(cacheDir, cache, - llb.AsPersistentCacheDir(g.CacheID(cacheDir), llb.CacheMountShared), llb.SourcePath("/cache/pip")) - run.AddMount(g.getWorkingDir(), - llb.Local(flag.FlagBuildContext)) - root = run.Root() - } - - if len(g.PythonWheels) > 0 { - root = root.Dir(g.getWorkingDir()) - cmdTemplate := "/opt/conda/envs/envd/bin/python -m pip install %s" - for _, wheel := range g.PythonWheels { - run := root.Run(llb.Shlexf(cmdTemplate, wheel), llb.WithCustomNamef("pip install %s", wheel)) - run.AddMount(g.getWorkingDir(), llb.Local(flag.FlagBuildContext), llb.Readonly) - run.AddMount(cacheDir, cache, - llb.AsPersistentCacheDir(g.CacheID(cacheDir), llb.CacheMountShared), llb.SourcePath("/cache/pip")) - root = run.Root() - } - } - return root -} - -func (g generalGraph) compilePyPIIndex(root llb.State) llb.State { - if g.PyPIIndexURL != nil { - logrus.WithField("index", *g.PyPIIndexURL).Debug("using custom PyPI index") - var extra string - if g.PyPIExtraIndexURL != nil { - logrus.WithField("index", *g.PyPIIndexURL).Debug("using extra PyPI index") - extra = "extra-index-url=" + *g.PyPIExtraIndexURL - } - if g.PyPITrust { - var hosts []string - for _, p := range []*string{g.PyPIIndexURL, g.PyPIExtraIndexURL} { - if p != nil { - u, err := url.Parse(*p) - if err == nil && u != nil && u.Hostname() != "" { - hosts = append(hosts, u.Hostname()) - } - } - } - if len(hosts) > 0 { - extra += "\ntrusted-host=" + strings.Join(hosts, " ") - } - } - content := fmt.Sprintf(pypiConfigTemplate, *g.PyPIIndexURL, extra) - dir := filepath.Dir(pypiIndexFilePath) - pypiMirror := root. - File(llb.Mkdir(dir, 0755, llb.WithParents(true), llb.WithUIDGID(g.uid, g.gid)), - llb.WithCustomNamef("[internal] setting PyPI index dir %s", dir)). - File(llb.Mkfile(pypiIndexFilePath, - 0644, []byte(content), llb.WithUIDGID(g.uid, g.gid)), - llb.WithCustomNamef("[internal] setting PyPI index file %s", pypiIndexFilePath)) - return pypiMirror - } - return root -} diff --git a/pkg/lang/ir/v0/r.go b/pkg/lang/ir/v0/r.go deleted file mode 100644 index d290ec34a..000000000 --- a/pkg/lang/ir/v0/r.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2022 The envd 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 v0 - -import ( - "fmt" - "strings" - - "github.com/cockroachdb/errors" - "github.com/moby/buildkit/client/llb" -) - -func (g generalGraph) compileRLang(baseStage llb.State) (llb.State, error) { - if err := g.compileJupyter(); err != nil { - return llb.State{}, errors.Wrap(err, "failed to compile jupyter") - } - aptStage := g.compileUbuntuAPT(baseStage) - builtinSystemStage := aptStage - - sshStage, err := g.copySSHKey(builtinSystemStage) - if err != nil { - return llb.State{}, errors.Wrap(err, "failed to copy ssh keys") - } - diffSSHStage := llb.Diff(builtinSystemStage, sshStage, llb.WithCustomName("install ssh keys")) - - // Conda affects shell and python, thus we cannot do it in parallel. - shellStage, err := g.compileShell(builtinSystemStage) - if err != nil { - return llb.State{}, errors.Wrap(err, "failed to compile shell") - } - diffShellStage := llb.Diff(builtinSystemStage, shellStage, llb.WithCustomName("install shell")) - - systemStage := llb.Diff(builtinSystemStage, g.compileSystemPackages(builtinSystemStage), - llb.WithCustomName("install system packages")) - - // TODO(terrytangyuan): Support RStudio local server - rPackageInstallStage := llb.Diff(builtinSystemStage, - g.installRPackages(builtinSystemStage), llb.WithCustomName("install R packages")) - - vscodeStage, err := g.compileVSCode() - if err != nil { - return llb.State{}, errors.Wrap(err, "failed to get vscode plugins") - } - - var merged llb.State - if vscodeStage != nil { - merged = llb.Merge([]llb.State{ - builtinSystemStage, systemStage, diffShellStage, - diffSSHStage, rPackageInstallStage, *vscodeStage, - }, llb.WithCustomName("[internal] generating the image")) - } else { - merged = llb.Merge([]llb.State{ - builtinSystemStage, systemStage, diffShellStage, - diffSSHStage, rPackageInstallStage, - }, llb.WithCustomName("[internal] generating the image")) - } - return merged, nil -} - -func (g generalGraph) installRPackages(root llb.State) llb.State { - if len(g.RPackages) == 0 { - return root - } - // TODO(terrytangyuan): Support different CRAN mirrors - var sb strings.Builder - mirrorURL := "https://cran.rstudio.com" - if g.CRANMirrorURL != nil { - mirrorURL = *g.CRANMirrorURL - } - sb.WriteString(fmt.Sprintf(`R -e 'options(repos = c(CRAN = "%s")); install.packages(c(`, mirrorURL)) - for i, pkg := range g.RPackages { - sb.WriteString(fmt.Sprintf(`"%s"`, pkg)) - if i != len(g.RPackages)-1 { - sb.WriteString(", ") - } - } - sb.WriteString(`))'`) - - // TODO(terrytangyuan): Support cache. - cmd := sb.String() - root = llb.User("envd")(root) - run := root.Run(llb.Shlex(cmd), llb.WithCustomNamef("install R packages")) - return run.Root() -} diff --git a/pkg/lang/ir/v0/shell.go b/pkg/lang/ir/v0/shell.go deleted file mode 100644 index 6035f0228..000000000 --- a/pkg/lang/ir/v0/shell.go +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2022 The envd 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 v0 - -import ( - "github.com/cockroachdb/errors" - "github.com/moby/buildkit/client/llb" - - "github.com/tensorchord/envd/pkg/flag" - "github.com/tensorchord/envd/pkg/progress/compileui" - "github.com/tensorchord/envd/pkg/shell" - "github.com/tensorchord/envd/pkg/util/fileutil" -) - -const ( - starshipConfig = ` -[container] -format = "[$symbol \\[envd\\]]($style)" - -[sudo] -disabled = false -symbol = "sudo " - -[python] -symbol = "Py " - -[status] -format = '[\[$status:$common_meaning$signal_name\]]($style) ' -disabled = false - -[git_branch] -symbol = "git " - -[git_commit] -tag_symbol = " tag " - -[git_status] -ahead = ">" -behind = "<" -diverged = "<>" -renamed = "r" -deleted = "x" -` -) - -func (g *generalGraph) compileShell(root llb.State) (llb.State, error) { - if g.Shell == shellZSH { - g.RuntimeEnviron["SHELL"] = "/usr/bin/zsh" - return g.compileZSH(root) - } - g.RuntimeEnviron["SHELL"] = "/usr/bin/bash" - return root, nil -} - -func (g *generalGraph) compileCondaShell(root llb.State) llb.State { - rcPath := fileutil.EnvdHomeDir(".bashrc") - if g.Shell == shellZSH { - rcPath = fileutil.EnvdHomeDir(".zshrc") - } - run := root. - Run(llb.Shlexf("bash -c \"%s\"", g.condaInitShell(g.Shell)), - llb.WithCustomNamef("[internal] init conda %s env", g.Shell)). - Run(llb.Shlexf(`bash -c 'echo "source %s/activate envd" >> %s'`, condaBinDir, rcPath), - llb.WithCustomNamef("[internal] add conda environment to %s", rcPath)) - return run.Root() -} - -func (g *generalGraph) compilePrompt(root llb.State) llb.State { - // skip this for customized image - if g.Image != nil { - return root - } - // starship config - config := root. - File(llb.Mkdir(defaultConfigDir, 0755, llb.WithParents(true)), - llb.WithCustomName("[internal] creating config dir")). - File(llb.Mkfile(starshipConfigPath, 0644, []byte(starshipConfig), llb.WithUIDGID(g.uid, g.gid)), - llb.WithCustomName("[internal] setting prompt starship config")) - - run := config.Run(llb.Shlexf(`bash -c 'echo "eval \"\$(starship init bash)\"" >> %s'`, fileutil.EnvdHomeDir(".bashrc")), - llb.WithCustomName("[internal] setting prompt bash config")).Root() - - if g.Shell == shellZSH { - run = run.Run( - llb.Shlexf(`bash -c 'echo "eval \"\$(starship init zsh)\"" >> %s'`, fileutil.EnvdHomeDir(".zshrc")), - llb.WithCustomName("[internal] setting prompt zsh config")).Root() - } - return run -} - -func (g generalGraph) compileZSH(root llb.State) (llb.State, error) { - installPath := fileutil.EnvdHomeDir("install.sh") - zshrcPath := fileutil.EnvdHomeDir(".zshrc") - ohMyZSHPath := fileutil.EnvdHomeDir(".oh-my-zsh") - m := shell.NewManager() - g.Writer.LogZSH(compileui.ActionStart, false) - if cached, err := m.DownloadOrCache(); err != nil { - return llb.State{}, errors.Wrap(err, "failed to download oh-my-zsh") - } else { - g.Writer.LogZSH(compileui.ActionEnd, cached) - } - zshStage := root. - File(llb.Copy(llb.Local(flag.FlagCacheDir), "oh-my-zsh", ohMyZSHPath, - &llb.CopyInfo{CreateDestPath: true}, llb.WithUIDGID(g.uid, g.gid))). - File(llb.Mkfile(installPath, - 0644, []byte(m.InstallScript()), llb.WithUIDGID(g.uid, g.gid))) - zshrc := zshStage.Run(llb.Shlexf("bash %s", installPath), - llb.WithCustomName("[internal] install oh-my-zsh")). - File(llb.Mkfile(zshrcPath, - 0644, []byte(m.ZSHRC()), llb.WithUIDGID(g.uid, g.gid))) - return zshrc, nil -} diff --git a/pkg/lang/ir/v0/supervisor.go b/pkg/lang/ir/v0/supervisor.go deleted file mode 100644 index 550500be8..000000000 --- a/pkg/lang/ir/v0/supervisor.go +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2022 The envd 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 v0 - -import ( - "fmt" - "path/filepath" - "strings" - - "github.com/cockroachdb/errors" - "github.com/moby/buildkit/client/llb" - - "github.com/tensorchord/envd/pkg/config" - "github.com/tensorchord/envd/pkg/types" -) - -const ( - horustTemplate = ` -name = "%[1]s" -command = """ -%[2]s -""" -stdout = "/var/log/horust/%[1]s_stdout.log" -stderr = "/var/log/horust/%[1]s_stderr.log" -user = "${USER}" -working-directory = "${%[3]s}" -%[4]s - -[environment] -keep-env = true - -[restart] -strategy = "on-failure" -backoff = "1s" -attempts = 2 - -[termination] -wait = "5s" -` -) - -func (g generalGraph) installHorust(root llb.State) llb.State { - horust := root. - File(llb.Copy(llb.Image(types.HorustImage), "/", "/usr/local/bin"), - llb.WithCustomName("[internal] install horust")). - File(llb.Mkdir(types.HorustServiceDir, 0755, llb.WithParents(true)), - llb.WithCustomNamef("[internal] mkdir for horust service: %s", types.HorustServiceDir)). - File(llb.Mkdir(types.HorustLogDir, 0777, llb.WithParents(true)), - llb.WithCustomNamef("[internal] mkdir for horust log: %s", types.HorustLogDir)) - return horust -} - -func (g generalGraph) addNewProcess(root llb.State, name, command string, depends []string) llb.State { - var sb strings.Builder - if len(depends) != 0 { - sb.WriteString("start-after = [") - for _, d := range depends { - sb.WriteString("\"") - sb.WriteString(d) - sb.WriteString("\",") - } - sb.WriteString("]\n") - } - template := fmt.Sprintf(horustTemplate, name, command, types.EnvdWorkDir, sb.String()) - - filename := filepath.Join(types.HorustServiceDir, fmt.Sprintf("%s.toml", name)) - supervisor := root.File(llb.Mkfile(filename, 0644, []byte(template), llb.WithUIDGID(g.uid, g.gid)), llb.WithCustomNamef("[internal] create file %s", filename)) - return supervisor -} - -func (g generalGraph) compileEntrypoint(root llb.State) (llb.State, error) { - if g.Image != nil { - return root, nil - } - if len(g.Entrypoint) > 0 { - return root, errors.New("`config.entrypoint` is only for custom image, maybe you need `runtime.init`") - } - cmd := fmt.Sprintf("/var/envd/bin/envd-sshd --port %d --shell %s", config.SSHPortInContainer, g.Shell) - entrypoint := g.addNewProcess(root, "sshd", cmd, nil) - var deps []string - if g.RuntimeInitScript != nil { - for i, command := range g.RuntimeInitScript { - entrypoint = g.addNewProcess(entrypoint, fmt.Sprintf("init_%d", i), fmt.Sprintf("/bin/bash -c 'set -euo pipefail\n%s'", strings.Join(command, "\n")), nil) - deps = append(deps, fmt.Sprintf("init_%d", i)) - } - } - - if g.RuntimeDaemon != nil { - for i, command := range g.RuntimeDaemon { - entrypoint = g.addNewProcess(entrypoint, fmt.Sprintf("daemon_%d", i), strings.Join(command, " "), deps) - } - } - - if g.JupyterConfig != nil { - jupyterCmd := g.generateJupyterCommand("") - entrypoint = g.addNewProcess(entrypoint, "jupyter", strings.Join(jupyterCmd, " "), deps) - } - - if g.RStudioServerConfig != nil { - rstudioCmd := g.generateRStudioCommand("") - entrypoint = g.addNewProcess(entrypoint, "rstudio", strings.Join(rstudioCmd, " "), deps) - } - - return entrypoint, nil -} diff --git a/pkg/lang/ir/v0/system.go b/pkg/lang/ir/v0/system.go deleted file mode 100644 index 98df4da62..000000000 --- a/pkg/lang/ir/v0/system.go +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright 2022 The envd 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 v0 - -import ( - "context" - _ "embed" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/cockroachdb/errors" - "github.com/moby/buildkit/client/llb" - "github.com/moby/buildkit/client/llb/imagemetaresolver" - "github.com/sirupsen/logrus" - "github.com/spf13/viper" - - "github.com/tensorchord/envd/pkg/config" - "github.com/tensorchord/envd/pkg/flag" - "github.com/tensorchord/envd/pkg/types" - "github.com/tensorchord/envd/pkg/util/fileutil" - "github.com/tensorchord/envd/pkg/version" -) - -func (g generalGraph) compileUbuntuAPT(root llb.State) llb.State { - if g.UbuntuAPTSource != nil { - logrus.WithField("source", *g.UbuntuAPTSource).Debug("using custom APT source") - aptSource := root. - File(llb.Mkdir(filepath.Dir(aptSourceFilePath), 0755, llb.WithParents(true)), - llb.WithCustomName("[internal] setting apt source")). - File(llb.Mkfile(aptSourceFilePath, 0644, []byte(*g.UbuntuAPTSource)), - llb.WithCustomName("[internal] setting apt source")) - return aptSource - } - return root -} - -func (g generalGraph) compileRun(root llb.State) llb.State { - if len(g.Exec) == 0 { - return root - } - - workingDir := g.getWorkingDir() - for _, execGroup := range g.Exec { - var sb strings.Builder - sb.WriteString("set -euo pipefail\n") - for _, c := range execGroup.Commands { - sb.WriteString(c + "\n") - } - - cmdStr := fmt.Sprintf("/usr/bin/bash -c '%s'", sb.String()) - logrus.WithField("command", cmdStr).Debug("compile run command") - // Mount the build context into the build process. - // TODO(gaocegege): Maybe we should make it readonly, - // but these cases then cannot be supported: - // run(commands=["git clone xx.git"]) - run := root.Dir(workingDir).Run(llb.Shlex(cmdStr)) - if execGroup.MountHost { - run.AddMount(workingDir, llb.Local(flag.FlagBuildContext)) - } - root = run.Root() - } - return root -} - -func (g generalGraph) compileCopy(root llb.State) llb.State { - if len(g.Copy) == 0 { - return root - } - - result := root - // Compose the copy command. - for _, c := range g.Copy { - result = result.File(llb.Copy( - llb.Local(flag.FlagBuildContext), c.Source, c.Destination, - llb.WithUIDGID(g.uid, g.gid))) - } - return result -} - -func (g *generalGraph) compileCUDAPackages(org string) llb.State { - return g.preparePythonBase(llb.Image(fmt.Sprintf( - "docker.io/%s:%s-cudnn%s-devel-%s", - org, *g.CUDA, g.CUDNN, g.OS))) -} - -func (g generalGraph) compileSystemPackages(root llb.State) llb.State { - if len(g.SystemPackages) == 0 { - logrus.Debug("skip the apt since system package is not specified") - return root - } - - // Compose the package install command. - var sb strings.Builder - sb.WriteString("sudo apt-get update && sudo apt-get install -y --no-install-recommends") - - for _, pkg := range g.SystemPackages { - sb.WriteString(fmt.Sprintf(" %s", pkg)) - } - - cacheDir := "/var/cache/apt" - cacheLibDir := "/var/lib/apt" - - run := root.Run(llb.Shlexf(`bash -c "%s"`, sb.String()), - llb.WithCustomNamef("apt-get install %s", - strings.Join(g.SystemPackages, " "))) - run.AddMount(cacheDir, llb.Scratch(), - llb.AsPersistentCacheDir(g.CacheID(cacheDir), llb.CacheMountShared)) - run.AddMount(cacheLibDir, llb.Scratch(), - llb.AsPersistentCacheDir(g.CacheID(cacheLibDir), llb.CacheMountShared)) - return run.Root() -} - -// nolint:unparam -func (g *generalGraph) compileExtraSource(root llb.State) (llb.State, error) { - if len(g.HTTP) == 0 { - return root, nil - } - inputs := []llb.State{} - for _, httpInfo := range g.HTTP { - src := llb.HTTP( - httpInfo.URL, - llb.Checksum(httpInfo.Checksum), - llb.Filename(httpInfo.Filename), - llb.Chown(g.uid, g.gid), - ) - inputs = append(inputs, llb.Scratch().File( - llb.Copy(src, "/", g.getExtraSourceDir(), &llb.CopyInfo{CreateDestPath: true}), - )) - } - inputs = append(inputs, root) - return llb.Merge(inputs, llb.WithCustomName("[internal] build source layers")), nil -} - -func (g *generalGraph) preparePythonBase(root llb.State) llb.State { - for _, env := range types.BaseEnvironment { - root = root.AddEnv(env.Name, env.Value) - } - - // apt packages - var sb strings.Builder - sb.WriteString("apt-get update && apt-get install -y apt-utils && ") - sb.WriteString("apt-get install -y --no-install-recommends --no-install-suggests --fix-missing ") - sb.WriteString(strings.Join(types.BaseAptPackage, " ")) - sb.WriteString("&& rm -rf /var/lib/apt/lists/* ") - // shell prompt - sb.WriteString("&& curl --proto '=https' --tlsv1.2 -sSf https://starship.rs/install.sh | sh -s -- -y") - sb.WriteString("&& locale-gen en_US.UTF-8") - - run := root.Run(llb.Shlexf(`bash -c "%s"`, sb.String()), - llb.WithCustomName("[internal] install built-in packages")) - - return run.Root() -} - -func (g generalGraph) compileSSHD(root llb.State) llb.State { - sshd := root.File(llb.Copy( - llb.Image(types.EnvdSshdImage), "/usr/bin/envd-sshd", "/var/envd/bin/envd-sshd", - &llb.CopyInfo{CreateDestPath: true}), - llb.WithCustomName(fmt.Sprintf("[internal] add envd-sshd from %s", types.EnvdSshdImage))) - return sshd -} - -func (g *generalGraph) compileBase() (llb.State, error) { - logger := logrus.WithFields(logrus.Fields{ - "os": g.OS, - "language": g.Language.Name, - }) - if g.Language.Version != nil { - logger = logger.WithField("version", *g.Language.Version) - } - logger.Debug("compile base image") - - var base llb.State - org := viper.GetString(flag.FlagDockerOrganization) - v := version.GetVersionForImageTag() - // Do not update user permission in the base image. - if g.Image != nil { - return g.customBase() - } else if g.CUDA == nil { - switch g.Language.Name { - case "r": - base = llb.Image(fmt.Sprintf("docker.io/%s/r-base:4.2-envd-%s", org, v)) - // r-base image already has GID 1000. - // It is a trick, we actually use GID 1000 - if g.gid == 1000 { - g.gid = 1001 - } - if g.uid == 1000 { - g.uid = 1001 - } - case "python": - // TODO(keming) use user input `base(os="")` - base = g.preparePythonBase(llb.Image(types.PythonBaseImage)) - case "julia": - base = llb.Image(fmt.Sprintf( - "docker.io/%s/julia:1.8rc1-ubuntu20.04-envd-%s", org, v)) - } - } else { - base = g.compileCUDAPackages("nvidia/cuda") - } - - // Install conda first. - condaStage := g.installConda(base) - supervisor := g.installHorust(condaStage) - sshdStage := g.compileSSHD(supervisor) - source, err := g.compileExtraSource(sshdStage) - if err != nil { - return llb.State{}, errors.Wrap(err, "failed to get extra sources") - } - final := g.compileUserGroup(source) - return final, nil -} - -// customBase get the image and the set the image metadata to graph. -func (g *generalGraph) customBase() (llb.State, error) { - if g.Image == nil { - return llb.State{}, fmt.Errorf("failed to get the image") - } - logrus.WithField("image", *g.Image).Debugf("using custom base image") - - // Fix https://github.com/tensorchord/envd/issues/1147. - // Fetch the image metadata from base image. - base := llb.Image(*g.Image, - llb.WithMetaResolver(imagemetaresolver.Default())) - envs, err := base.Env(context.Background()) - if err != nil { - return llb.State{}, errors.Wrap(err, "failed to get the image metadata") - } - - // Set the environment variables to RuntimeEnviron to keep it in the resulting image. - for _, e := range envs { - // in case the env value also contains `=` - kv := strings.SplitN(e, "=", 2) - g.RuntimeEnviron[kv[0]] = kv[1] - } - return base, nil -} - -func (g generalGraph) copySSHKey(root llb.State) (llb.State, error) { - public := g.PublicKeyPath - bdat, err := os.ReadFile(public) - dat := strings.TrimSuffix(string(bdat), "\n") - if err != nil { - return llb.State{}, errors.Wrap(err, "Cannot read public SSH key") - } - run := root. - File(llb.Mkdir("/var/envd", 0755, llb.WithParents(true), - llb.WithUIDGID(g.uid, g.gid)), - llb.WithCustomName("[internal] create dir for ssh key")). - File(llb.Mkfile(config.ContainerAuthorizedKeysPath, - 0644, []byte(dat+" envd"), llb.WithUIDGID(g.uid, g.gid)), - llb.WithCustomName("[internal] install ssh keys")) - return run, nil -} - -func (g generalGraph) compileMountDir(root llb.State) llb.State { - mount := root - if g.Image == nil { - // create the ENVD_WORKDIR as a placeholder (envd-server may not mount this dir) - workDir := fileutil.EnvdHomeDir(g.EnvironmentName) - mount = root.File(llb.Mkdir(workDir, 0755, llb.WithParents(true), llb.WithUIDGID(g.uid, g.gid)), - llb.WithCustomNamef("[internal] create work dir: %s", workDir)) - } - - for _, m := range g.Mount { - mount = mount.File(llb.Mkdir(m.Destination, 0755, llb.WithParents(true), - llb.WithUIDGID(g.uid, g.gid)), - llb.WithCustomNamef("[internal] create dir for runtime.mount %s", m.Destination), - ) - } - return mount -} - -func (g *generalGraph) updateEnvPath(root llb.State, path string) llb.State { - g.RuntimeEnvPaths = append(g.RuntimeEnvPaths, path) - return root.AddEnv("PATH", strings.Join(g.RuntimeEnvPaths, ":")) -} diff --git a/pkg/lang/ir/v0/types.go b/pkg/lang/ir/v0/types.go deleted file mode 100644 index ce0db91ec..000000000 --- a/pkg/lang/ir/v0/types.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2022 The envd 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 v0 - -import ( - "github.com/tensorchord/envd/pkg/editor/vscode" - "github.com/tensorchord/envd/pkg/lang/ir" - "github.com/tensorchord/envd/pkg/progress/compileui" - "github.com/tensorchord/envd/pkg/types" -) - -// A Graph contains the state, -// such as its call stack and thread-local storage. -// TODO(gaocegeg): Refactor it to support order. -type generalGraph struct { - uid int - gid int - - OS string - ir.Language - Image *string - - Shell string - CUDA *string - CUDNN string - NumGPUs int - - UbuntuAPTSource *string - CRANMirrorURL *string - JuliaPackageServer *string - PyPIIndexURL *string - PyPIExtraIndexURL *string - PyPITrust bool - - PublicKeyPath string - - PyPIPackages [][]string - RequirementsFile *string - PythonWheels []string - RPackages []string - JuliaPackages []string - SystemPackages []string - - VSCodePlugins []vscode.Plugin - UserDirectories []string - - Exec []ir.RunBuildCommand - Copy []ir.CopyInfo - Mount []ir.MountInfo - HTTP []ir.HTTPInfo - Entrypoint []string - - Repo types.RepoInfo - - *ir.JupyterConfig - *ir.GitConfig - *ir.CondaConfig - *ir.RStudioServerConfig - - Writer compileui.Writer - // EnvironmentName is the base name of the environment. - // It is the BaseDir(BuildContextDir) - // e.g. mnist, streamlit-mnist - EnvironmentName string - - ir.RuntimeGraph -} - -const ( - shellBASH = "bash" - shellZSH = "zsh" -) diff --git a/pkg/lang/ir/v0/user.go b/pkg/lang/ir/v0/user.go deleted file mode 100644 index 2a24cbdda..000000000 --- a/pkg/lang/ir/v0/user.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2022 The envd 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 v0 - -import ( - "strings" - - "github.com/moby/buildkit/client/llb" - - "github.com/tensorchord/envd/pkg/types" -) - -// compileUserOwn chown related directories -func (g *generalGraph) compileUserOwn(root llb.State) llb.State { - if g.Image != nil || g.uid == 0 { - g.RuntimeEnviron["USER"] = "root" - return root - } - g.RuntimeEnviron["USER"] = "envd" - for _, dir := range g.UserDirectories { - root = root.Run(llb.Shlexf("chown -R envd:envd %s", dir), - llb.WithCustomNamef("[internal] configure user permissions for %s", dir)).Root() - } - user := root.User("envd") - // re-add the env since it's a different user - for _, env := range types.BaseEnvironment { - user = user.AddEnv(env.Name, env.Value) - } - user.AddEnv("PATH", strings.Join(g.RuntimeEnvPaths, ":")) - return user -} - -// compileUserGroup creates user `envd` -func (g *generalGraph) compileUserGroup(root llb.State) llb.State { - if g.Image != nil { - return root - } - var res llb.ExecState - if g.uid == 0 { - res = root. - Run(llb.Shlexf("groupadd -g %d envd", 1001), - llb.WithCustomName("[internal] still create group envd for root context")). - Run(llb.Shlexf(`useradd -p "" -u %d -g envd -s /bin/sh -m envd`, 1001), - llb.WithCustomName("[internal] still create user envd for root context")). - Run(llb.Shlex("usermod -s /bin/sh root"), - llb.WithCustomName("[internal] set root default shell to /bin/sh")). - Run(llb.Shlex("sed -i \"s/envd:x:1001:1001/envd:x:0:0/g\" /etc/passwd"), - llb.WithCustomName("[internal] set envd uid to 0 as root")). - Run(llb.Shlex("sed -i \"s./root./home/envd.g\" /etc/passwd"), - llb.WithCustomName("[internal] set root home dir to /home/envd")). - Run(llb.Shlex("sed -i \"s/envd:x:1001/envd:x:0/g\" /etc/group"), - llb.WithCustomName("[internal] set envd group to 0 as root group")) - } else { - res = root. - Run(llb.Shlexf(`groupadd -g %d envd`, g.gid), - llb.WithCustomName("[internal] create user group envd")). - Run(llb.Shlexf(`useradd -p "" -u %d -g envd -s /bin/sh -m envd`, g.uid), - llb.WithCustomName("[internal] create user envd")). - Run(llb.Shlex("adduser envd sudo"), - llb.WithCustomName("[internal] add user envd to sudoers")). - Run(llb.Shlexf("install -d -o envd -g %d -m 0700 /home/envd/.config /home/envd/.cache", g.gid), - llb.WithCustomName("[internal] mkdir config and cache dir")) - } - return res.Root() -} diff --git a/pkg/lang/ir/v0/util.go b/pkg/lang/ir/v0/util.go deleted file mode 100644 index 2339b6a44..000000000 --- a/pkg/lang/ir/v0/util.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2022 The envd 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 v0 - -import ( - "bytes" - "crypto/md5" - "encoding/gob" - "encoding/hex" - "os/user" - "regexp" - "strconv" - "strings" - - "github.com/cockroachdb/errors" - "github.com/sirupsen/logrus" - "github.com/spf13/viper" - - "github.com/tensorchord/envd/pkg/flag" - "github.com/tensorchord/envd/pkg/util/fileutil" -) - -func (g generalGraph) getWorkingDir() string { - return fileutil.EnvdHomeDir(g.EnvironmentName) -} - -func (g generalGraph) getExtraSourceDir() string { - return fileutil.EnvdHomeDir("extra_source") -} - -func parseLanguage(l string) (string, *string, error) { - var language, version string - if l == "" { - return "", nil, errors.New("language is required") - } - - // Get version from the string. - re := regexp.MustCompile(`\d[\d,]*[\.]?[\d{2}]*[\.]?[\d{2}]*`) - if !re.MatchString(l) { - language = l - } else { - loc := re.FindStringIndex(l) - language = l[:loc[0]] - version = l[loc[0]:] - } - - switch language { - case "python", "r", "julia": - return language, &version, nil - default: - return "", nil, errors.Newf("language %s is not supported", language) - } -} - -func getUIDGID() (int, int, error) { - owner := viper.GetString(flag.FlagBuildOwner) - if len(owner) > 0 { - logrus.WithField("flag", owner).Info("use owner") - ids := strings.Split(owner, ":") - if len(ids) > 2 { - return 0, 0, errors.Newf("wrong format for owner (uid:gid): %s", owner) - } - uid, err := strconv.Atoi(ids[0]) - if err != nil { - logrus.Info(err) - return 0, 0, errors.Wrap(err, "failed to get uid") - } - // if omit gid, will use the uid as gid - if len(ids) == 1 { - return uid, uid, nil - } - gid, err := strconv.Atoi(ids[1]) - if err != nil { - return 0, 0, errors.Wrap(err, "failed to get gid") - } - return uid, gid, nil - } - user, err := user.Current() - if err != nil { - return 0, 0, errors.Wrap(err, "failed to get uid/gid") - } - // Do not support windows yet. - uid, err := strconv.Atoi(user.Uid) - if err != nil { - return 0, 0, errors.Wrap(err, "failed to get uid") - } - gid, err := strconv.Atoi(user.Gid) - if err != nil { - return 0, 0, errors.Wrap(err, "failed to get gid") - } - return uid, gid, nil -} - -// A stream of gobs is self-describing. Each data item in the stream is preceded by a specification of its type, expressed in terms of a small set of predefined types. Pointers are not transmitted, but the things they point to are transmitted; that is, the values are flattened. -// see https://pkg.go.dev/encoding/gob#hdr-Basics -// we hash the blobs to determined if the graph changed. -func GetDefaultGraphHash() string { - var b bytes.Buffer - err := gob.NewEncoder(&b).Encode(DefaultGraph) - if err != nil { - return "" - } - data := b.Bytes() - hashD := md5.Sum(data) - return hex.EncodeToString(hashD[:]) -} diff --git a/pkg/lang/ir/v0/util_test.go b/pkg/lang/ir/v0/util_test.go deleted file mode 100644 index 255e0ea24..000000000 --- a/pkg/lang/ir/v0/util_test.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2022 The envd 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 v0 - -import "testing" - -func TestParseLanguage(t *testing.T) { - tcs := []struct { - l string - ExpectedLanguage string - ExpectedVersion string - ExpectedError bool - }{ - { - l: "python", - ExpectedLanguage: "python", - ExpectedVersion: "", - ExpectedError: false, - }, - { - l: "python3.7", - ExpectedLanguage: "python", - ExpectedVersion: "3.7", - ExpectedError: false, - }, - { - l: "python3.7.1", - ExpectedLanguage: "python", - ExpectedVersion: "3.7.1", - ExpectedError: false, - }, - { - l: "python-3.7.1", - ExpectedError: true, - }, - { - l: "r", - ExpectedLanguage: "r", - ExpectedVersion: "", - ExpectedError: false, - }, - } - - for _, tc := range tcs { - language, version, err := parseLanguage(tc.l) - if err != nil { - if !tc.ExpectedError { - t.Errorf("parseLanguage(%s) returned error: %v", tc.l, err) - } - } else { - if language != tc.ExpectedLanguage { - t.Errorf("parseLanguage(%s) returned language %s, expected %s", tc.l, language, tc.ExpectedLanguage) - } - if version == nil { - if tc.ExpectedVersion != "" { - t.Errorf("parseLanguage(%s) returned version nil, expected %s", tc.l, tc.ExpectedVersion) - } - } else { - if *version != tc.ExpectedVersion { - t.Errorf("parseLanguage(%s) returned version %s, expected %s", tc.l, *version, tc.ExpectedVersion) - } - } - } - - } -} diff --git a/pkg/lang/ir/v1/cache.go b/pkg/lang/ir/v1/cache.go index 1364a6193..5268c1c87 100644 --- a/pkg/lang/ir/v1/cache.go +++ b/pkg/lang/ir/v1/cache.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/lang/ir/v1/checker.go b/pkg/lang/ir/v1/checker.go index f152ec579..a4583ce81 100644 --- a/pkg/lang/ir/v1/checker.go +++ b/pkg/lang/ir/v1/checker.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/lang/ir/v1/compile.go b/pkg/lang/ir/v1/compile.go index d86dd57db..842dc2d62 100644 --- a/pkg/lang/ir/v1/compile.go +++ b/pkg/lang/ir/v1/compile.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -24,30 +24,36 @@ import ( "github.com/cockroachdb/errors" "github.com/moby/buildkit/client/llb" + ocispecs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/sirupsen/logrus" + "github.com/spf13/viper" servertypes "github.com/tensorchord/envd-server/api/types" "github.com/tensorchord/envd/pkg/config" + "github.com/tensorchord/envd/pkg/flag" + "github.com/tensorchord/envd/pkg/home" "github.com/tensorchord/envd/pkg/lang/ir" "github.com/tensorchord/envd/pkg/progress/compileui" "github.com/tensorchord/envd/pkg/types" "github.com/tensorchord/envd/pkg/util/fileutil" + "github.com/tensorchord/envd/pkg/version" ) func NewGraph() ir.Graph { runtimeGraph := ir.RuntimeGraph{ RuntimeCommands: make(map[string]string), RuntimeEnviron: make(map[string]string), - RuntimeEnvPaths: []string{types.DefaultSystemPath}, + RuntimeEnvPaths: strings.Split(types.DefaultSystemPath, ":"), } return &generalGraph{ - uid: -1, - gid: -1, - Image: defaultImage, - Language: ir.Language{}, - CUDA: nil, - CUDNN: CUDNNVersionDefault, - NumGPUs: 0, + uid: -1, + gid: -1, + Image: defaultImage, + CUDA: nil, + CUDNN: CUDNNVersionDefault, + NumGPUs: 0, + ShmSize: 0, + EnvdSyntaxVersion: "v1", PyPIPackages: [][]string{}, RPackages: [][]string{}, @@ -57,6 +63,8 @@ func NewGraph() ir.Graph { UserDirectories: []string{}, Shell: shellBASH, RuntimeGraph: runtimeGraph, + Platform: &ocispecs.Platform{}, + WorkingDir: "/", } } @@ -66,10 +74,18 @@ func (g *generalGraph) SetWriter(w compileui.Writer) { g.Writer = w } +func (g generalGraph) IsDev() bool { + return g.Dev +} + func (g generalGraph) GetHTTP() []ir.HTTPInfo { return g.HTTP } +func (g generalGraph) GetShmSize() int { + return g.ShmSize +} + func (g generalGraph) GetNumGPUs() int { return g.NumGPUs } @@ -102,14 +118,32 @@ func (g generalGraph) GetRuntimeCommands() map[string]string { return g.RuntimeCommands } -func (g *generalGraph) Compile(ctx context.Context, envName string, pub string) (*llb.Definition, error) { - w, err := compileui.New(ctx, os.Stdout, "auto") +func (g generalGraph) GetPlatform() *ocispecs.Platform { + return g.Platform +} + +func (g generalGraph) GetWorkingDir() string { + return g.WorkingDir +} + +func (g *generalGraph) Compile(ctx context.Context, envPath string, pub string, platform *ocispecs.Platform, progressMode string) (*llb.Definition, error) { + w, err := compileui.New(ctx, os.Stdout, progressMode) if err != nil { return nil, errors.Wrap(err, "failed to create compileui") } + c, err := home.GetManager().ContextGetCurrent() + if err != nil { + return nil, errors.Wrap(err, "failed to get the current envd context") + } + g.Writer = w - g.EnvironmentName = envName + g.EnvironmentPath = envPath + g.EnvironmentName = filepath.Base(envPath) g.PublicKeyPath = pub + g.Platform = platform + if c.Builder == types.BuilderTypeMoby { + g.DisableMergeOp = true + } uid, gid, err := g.getUIDGID() if err != nil { @@ -119,8 +153,7 @@ func (g *generalGraph) Compile(ctx context.Context, envName string, pub string) if err != nil { return nil, errors.Wrap(err, "failed to compile the graph") } - // TODO(gaocegege): Support multi platform. - def, err := state.Marshal(ctx, llb.LinuxAmd64) + def, err := state.Marshal(ctx, llb.Platform(*g.Platform)) if err != nil { return nil, errors.Wrap(err, "failed to marshal the llb definition") } @@ -129,7 +162,7 @@ func (g *generalGraph) Compile(ctx context.Context, envName string, pub string) func (g generalGraph) GetEnviron() []string { return append(g.EnvString(), - "LC_ALL=en_US.UTF-8", + "LC_ALL=C.UTF-8", "LANG=C.UTF-8", ) } @@ -144,6 +177,9 @@ func (g generalGraph) GPUEnabled() bool { func (g generalGraph) Labels() (map[string]string, error) { labels := make(map[string]string) + + labels[types.ImageLabelSyntaxVer] = g.EnvdSyntaxVersion + str, err := json.Marshal(g.SystemPackages) if err != nil { return nil, err @@ -175,6 +211,12 @@ func (g generalGraph) Labels() (map[string]string, error) { } labels[types.RuntimeGraphCode] = code + code, err = g.Dump() + if err != nil { + return labels, err + } + labels[types.GeneralGraphCode] = code + ports := []servertypes.EnvironmentPort{} ports = append(ports, servertypes.EnvironmentPort{ Name: "ssh", @@ -193,7 +235,7 @@ func (g generalGraph) Labels() (map[string]string, error) { }) } - if g.RuntimeExpose != nil && len(g.RuntimeExpose) > 0 { + if len(g.RuntimeExpose) > 0 { for _, item := range g.RuntimeExpose { ports = append(ports, servertypes.EnvironmentPort{ Name: item.ServiceName, @@ -214,7 +256,7 @@ func (g generalGraph) Labels() (map[string]string, error) { } labels[types.ImageLabelRepo] = string(repoInfo) - labels[types.ImageLabelContainerName] = string(g.EnvironmentName) + labels[types.ImageLabelContainerName] = g.EnvironmentName return labels, nil } @@ -234,7 +276,7 @@ func (g generalGraph) ExposedPorts() (map[string]struct{}, error) { ports[fmt.Sprintf("%d/tcp", config.RStudioServerPortInContainer)] = struct{}{} } - if g.RuntimeExpose != nil && len(g.RuntimeExpose) > 0 { + if len(g.RuntimeExpose) > 0 { for _, item := range g.RuntimeExpose { ports[fmt.Sprintf("%d/tcp", item.EnvdPort)] = struct{}{} } @@ -246,6 +288,9 @@ func (g generalGraph) ExposedPorts() (map[string]struct{}, error) { func (g generalGraph) EnvString() []string { var envs []string for k, v := range g.RuntimeEnviron { + if k == "PATH" { + continue + } envs = append(envs, fmt.Sprintf("%s=%s", k, v)) } envs = append(envs, fmt.Sprintf("PATH=%s", strings.Join(g.RuntimeEnvPaths, ":"))) @@ -253,8 +298,23 @@ func (g generalGraph) EnvString() []string { } func (g generalGraph) DefaultCacheImporter() (*string, error) { - // We don't have remote cache for v1 - return nil, nil + // base image cache with python + conda for dev env + if !g.Dev { + return nil, nil + } + var res string + if g.CUDA != nil { + res = fmt.Sprintf( + "type=registry,ref=docker.io/%s/python-cache:envd-%s-cuda-%s-cudnn-%s", + viper.GetString(flag.FlagDockerOrganization), + version.GetVersionForImageTag(), *g.CUDA, g.CUDNN) + } else { + res = fmt.Sprintf( + "type=registry,ref=docker.io/%s/python-cache:envd-%s", + viper.GetString(flag.FlagDockerOrganization), + version.GetVersionForImageTag()) + } + return &res, nil } func (g *generalGraph) GetEntrypoint(buildContextDir string) ([]string, error) { @@ -277,36 +337,42 @@ func (g *generalGraph) CompileLLB(uid, gid int) (llb.State, error) { if err != nil { return llb.State{}, errors.Wrap(err, "failed to get the base image") } + aptMirror := g.compileUbuntuAPT(base) // prepare dev env: stable operations should be done here to make it cache friendly if g.Dev { - dev := g.compileDevPackages(base) + dev := g.compileDevPackages(aptMirror) sshd := g.compileSSHD(dev) horust := g.installHorust(sshd) - userGroup := g.compileUserGroup(horust) - base = userGroup + starship := g.compileStarship(horust) + userGroup := g.compileUserGroup(starship) + aptMirror = userGroup } - lang, err := g.compileLanguage(base) - if err != nil { - return llb.State{}, errors.Wrap(err, "failed to compile language") - } - aptMirror := g.compileUbuntuAPT(base) systemPackages := g.compileSystemPackages(aptMirror) - merge := llb.Merge([]llb.State{ - base, - llb.Diff(base, lang, llb.WithCustomName("[internal] prepare language")), - llb.Diff(base, systemPackages, llb.WithCustomName("[internal] install system packages")), - }, llb.WithCustomName("[internal] language environment and system packages")) - packages := g.compileLanguagePackages(merge) + var language llb.State + if g.DisableMergeOp { + language, err = g.compileLanguage(systemPackages) + if err != nil { + return llb.State{}, errors.Wrap(err, "failed to compile language from system packages") + } + } else { + lang, err := g.compileLanguage(aptMirror) + if err != nil { + return llb.State{}, errors.Wrap(err, "failed to compile language from apt mirror") + } + language = llb.Merge([]llb.State{ + base, + llb.Diff(base, systemPackages, llb.WithCustomName("[internal] install system packages")), + llb.Diff(base, lang, llb.WithCustomName("[internal] prepare language")), + }, llb.WithCustomName("[internal] language environment and system packages")) + } + packages := g.compileLanguagePackages(language) if err != nil { return llb.State{}, errors.Wrap(err, "failed to compile language") } - source, err := g.compileExtraSource(packages) - if err != nil { - return llb.State{}, errors.Wrap(err, "failed to compile extra source") - } + source := g.compileExtraSource(packages) copy := g.compileCopy(source) // dev postprocessing: related to UID, which may not be cached @@ -322,18 +388,19 @@ func (g *generalGraph) CompileLLB(uid, gid int) (llb.State, error) { return llb.State{}, errors.Wrap(err, "failed to compile shell") } prompt := g.compilePrompt(shell) + if g.UVConfig != nil { + // re-install uv Python for dev user + prompt = g.compileUVPython(prompt) + } entrypoint, err := g.compileEntrypoint(prompt) if err != nil { return llb.State{}, errors.Wrap(err, "failed to compile entrypoint") } - vscode, err := g.compileVSCode() + vscode, err := g.compileVSCode(entrypoint) if err != nil { return llb.State{}, errors.Wrap(err, "failed to compile VSCode extensions") } - copy = llb.Merge([]llb.State{ - entrypoint, - vscode, - }, llb.WithCustomName("[internal] final dev environment")) + copy = vscode } // it's necessary to exec `run` with the desired user diff --git a/pkg/lang/ir/v1/conda.go b/pkg/lang/ir/v1/conda.go index 5954df665..4bddf9d13 100644 --- a/pkg/lang/ir/v1/conda.go +++ b/pkg/lang/ir/v1/conda.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -28,9 +28,9 @@ import ( ) const ( - builderImage = "curlimages/curl:7.86.0" - condaVersionDefault = "py39_4.11.0" - microMambaImage = "mambaorg/micromamba:1.0.0" + builderImage = "curlimages/curl:8.11.1" + condaVersionDefault = "py311_25.1.1-2" + microMambaImage = "mambaorg/micromamba:2.0.6" condaRootPrefix = "/opt/conda" condaBinDir = "/opt/conda/bin" condaSourcePath = "/tmp/miniconda.sh" @@ -39,10 +39,19 @@ const ( channels: - defaults ` - mambaActivate = ` + mambaActivateBash = ` #!/bin/sh eval "$(/opt/conda/bin/micromamba shell hook --shell=bash)" || return $? micromamba activate "$@" +` + mambaActivateFish = ` +#!/usr/bin/fish +/opt/conda/bin/micromamba shell hook --shell=fish | source +micromamba activate $argv +` + condaActivateFish = ` +#!/usr/bin/fish +conda activate $argv ` ) @@ -77,7 +86,7 @@ func (g generalGraph) condaCommandPath() string { func (g generalGraph) condaInitShell(shell string) string { path := g.condaCommandPath() if g.CondaConfig.UseMicroMamba { - return fmt.Sprintf("%s shell init -p %s -s %s", path, condaRootPrefix, shell) + return fmt.Sprintf("%s shell init -s %s %s", path, shell, condaRootPrefix) } return fmt.Sprintf("%s init %s", path, shell) } @@ -154,8 +163,8 @@ func (g generalGraph) compileCondaEnvironment(root llb.State) (llb.State, error) func (g *generalGraph) installConda(root llb.State) llb.State { if g.Dev { // We only create envd user for dev env. - // This directory is related to conda envd env meta (used by `conda env config vars set key=value`) - g.UserDirectories = append(g.UserDirectories, fmt.Sprintf("%s/envs/envd/conda-meta", condaRootPrefix)) + // `micromamba` needs to modify the conda directory. + g.UserDirectories = append(g.UserDirectories, condaRootPrefix) } if g.CondaConfig.UseMicroMamba { return g.installMicroMamba(root) @@ -175,6 +184,8 @@ func (g generalGraph) installMiniConda(root llb.State) llb.State { llb.WithCustomName("[internal] create conda directory")). Run(llb.Shlexf("bash -c '%s'", installCondaBash), llb.WithCustomName("[internal] install conda")).Root(). + File(llb.Mkfile(fmt.Sprintf("%s/activate.fish", condaBinDir), 0755, []byte(condaActivateFish)), + llb.WithCustomName("[internal] create the conda activate.fish file")). File(llb.Rm(condaSourcePath), llb.WithCustomName("[internal] rm conda source file")) return conda } @@ -195,8 +206,10 @@ func (g *generalGraph) installMicroMamba(root llb.State) llb.State { llb.WithCustomName("[internal] copy micromamba binary")). File(llb.Mkfile(fmt.Sprintf("%s/.mambarc", condaRootPrefix), 0644, []byte(mambaRc)), llb.WithCustomName("[internal] create the mamba rc file")). - File(llb.Mkfile(fmt.Sprintf("%s/activate", condaBinDir), 0755, []byte(mambaActivate)), + File(llb.Mkfile(fmt.Sprintf("%s/activate", condaBinDir), 0755, []byte(mambaActivateBash)), llb.WithCustomName("[internal] create the mamba activate file")). + File(llb.Mkfile(fmt.Sprintf("%s/activate.fish", condaBinDir), 0755, []byte(mambaActivateFish)), + llb.WithCustomName("[internal] create the mamba activate.fish file")). Run(llb.Shlexf("update-alternatives --install /usr/bin/conda conda %s/micromamba 1", condaBinDir), llb.WithCustomName("[internal] update alternative micromamba to conda")). Run(llb.Shlexf("bash -c \"%s/micromamba shell init --shell bash\"", condaBinDir), diff --git a/pkg/lang/ir/v1/consts.go b/pkg/lang/ir/v1/consts.go index c7769e30b..75b7ab566 100644 --- a/pkg/lang/ir/v1/consts.go +++ b/pkg/lang/ir/v1/consts.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ package v1 import "github.com/tensorchord/envd/pkg/util/fileutil" const ( - defaultImage = "ubuntu:20.04" + defaultImage = "ubuntu:22.04" CUDNNVersionDefault = "8" aptSourceFilePath = "/etc/apt/sources.list" diff --git a/pkg/lang/ir/v1/editor.go b/pkg/lang/ir/v1/editor.go index a92e87ba9..613d2be2b 100644 --- a/pkg/lang/ir/v1/editor.go +++ b/pkg/lang/ir/v1/editor.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -29,48 +29,42 @@ import ( "github.com/tensorchord/envd/pkg/util/fileutil" ) -func (g generalGraph) compileVSCode() (llb.State, error) { - if len(g.VSCodePlugins) == 0 { - return llb.Scratch(), nil +func (g generalGraph) compileVSCode(root llb.State) (llb.State, error) { + platform, err := vscode.ConvertLLBPlatform(g.Platform) + if err != nil { + return llb.State{}, errors.Wrap(err, "failed to convert llb platform") } - inputs := []llb.State{} for _, p := range g.VSCodePlugins { + p.Platform = platform vscodeClient, err := vscode.NewClient(vscode.MarketplaceVendorOpenVSX) if err != nil { return llb.State{}, errors.Wrap(err, "failed to create vscode client") } g.Writer.LogVSCodePlugin(p, compileui.ActionStart, false) - if cached, err := vscodeClient.DownloadOrCache(p); err != nil { + cached, err := vscodeClient.DownloadOrCache(p) + if err != nil { return llb.State{}, err - } else { - g.Writer.LogVSCodePlugin(p, compileui.ActionEnd, cached) } - ext := llb.Scratch().File(llb.Copy(llb.Local(flag.FlagCacheDir), + g.Writer.LogVSCodePlugin(p, compileui.ActionEnd, cached) + root = root.File(llb.Copy(llb.Local(flag.FlagCacheDir), vscodeClient.PluginPath(p), fileutil.EnvdHomeDir(".vscode-server", "extensions", p.String()), &llb.CopyInfo{ CreateDestPath: true, }, llb.WithUIDGID(g.uid, g.gid)), llb.WithCustomNamef("install vscode plugin %s", p.String())) - inputs = append(inputs, ext) } - layer := llb.Merge(inputs, llb.WithCustomName("merging plugins for vscode")) - return layer, nil + return root, nil } // nolint:unused -func (g *generalGraph) compileJupyter() error { +func (g *generalGraph) compileJupyter() { if g.JupyterConfig == nil { - return nil + return } + // no need to check if `python` is installed since v1 should support user costumed image g.PyPIPackages = append(g.PyPIPackages, []string{"jupyter"}) - switch g.Language.Name { - case "python": - return nil - default: - return errors.Newf("Jupyter is not supported in %s yet", g.Language.Name) - } } func (g generalGraph) generateJupyterCommand(workingDir string) []string { diff --git a/pkg/lang/ir/v1/editor_test.go b/pkg/lang/ir/v1/editor_test.go index dfb41cdc1..935664a5c 100644 --- a/pkg/lang/ir/v1/editor_test.go +++ b/pkg/lang/ir/v1/editor_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/lang/ir/v1/fs.go b/pkg/lang/ir/v1/fs.go index 2a16ddf92..72043ea39 100644 --- a/pkg/lang/ir/v1/fs.go +++ b/pkg/lang/ir/v1/fs.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/lang/ir/v1/get_conda.sh b/pkg/lang/ir/v1/get_conda.sh index c3dbbcdd2..17c6caff4 100644 --- a/pkg/lang/ir/v1/get_conda.sh +++ b/pkg/lang/ir/v1/get_conda.sh @@ -2,16 +2,13 @@ set -euo pipefail && \ UNAME_M="$(uname -m)" && \ if [ "${UNAME_M}" = "x86_64" ]; then \ MINICONDA_URL="https://repo.anaconda.com/miniconda/Miniconda3-${CONDA_VERSION}-Linux-x86_64.sh"; \ - SHA256SUM="4ee9c3aa53329cd7a63b49877c0babb49b19b7e5af29807b793a76bdb1d362b4"; \ + SHA256SUM="d8c1645776c0758214e4191c605abe5878002051316bd423f2b14b22d6cb4251"; \ elif [ "${UNAME_M}" = "s390x" ]; then \ MINICONDA_URL="https://repo.anaconda.com/miniconda/Miniconda3-${CONDA_VERSION}-Linux-s390x.sh"; \ - SHA256SUM="e5e5e89cdcef9332fe632cd25d318cf71f681eef029a24495c713b18e66a8018"; \ + SHA256SUM="0b4d5a3f16dcb2d230ba5dfdfdb848c854006aab6dd1bd3dbf29fcddf04b07a4"; \ elif [ "${UNAME_M}" = "aarch64" ]; then \ MINICONDA_URL="https://repo.anaconda.com/miniconda/Miniconda3-${CONDA_VERSION}-Linux-aarch64.sh"; \ - SHA256SUM="00c7127a8a8d3f4b9c2ab3391c661239d5b9a88eafe895fd0f3f2a8d9c0f4556"; \ -elif [ "${UNAME_M}" = "ppc64le" ]; then \ - MINICONDA_URL="https://repo.anaconda.com/miniconda/Miniconda3-${CONDA_VERSION}-Linux-ppc64le.sh"; \ - SHA256SUM="8ee1f8d17ef7c8cb08a85f7d858b1cb55866c06fcf7545b98c3b82e4d0277e66"; \ + SHA256SUM="8a1d4407fce7ec552ac6ed655ce93d83549e02b819cacefbb7f640f9051e638b"; \ fi && \ wget "${MINICONDA_URL}" -O /tmp/miniconda.sh && \ echo "${SHA256SUM} /tmp/miniconda.sh" > /tmp/shasum && \ diff --git a/pkg/lang/ir/v1/git.go b/pkg/lang/ir/v1/git.go index 8e456efb0..25acd31a2 100644 --- a/pkg/lang/ir/v1/git.go +++ b/pkg/lang/ir/v1/git.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/lang/ir/v1/interface.go b/pkg/lang/ir/v1/interface.go index 9e4ba594e..0360219a0 100644 --- a/pkg/lang/ir/v1/interface.go +++ b/pkg/lang/ir/v1/interface.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -43,10 +43,10 @@ func Python(version string) error { } g := DefaultGraph.(*generalGraph) - g.Language = ir.Language{ + g.Languages = append(g.Languages, ir.Language{ Name: "python", Version: &version, - } + }) return nil } @@ -58,20 +58,40 @@ func Conda(mamba bool) { } } +func Pixi(usePixiMirror bool, pypiIndex string) { + g := DefaultGraph.(*generalGraph) + + g.PixiConfig = &ir.PixiConfig{ + UsePixiMirror: usePixiMirror, + PyPIIndex: nil, + } + if len(pypiIndex) != 0 { + g.PixiConfig.PyPIIndex = &pypiIndex + } +} + +func UV(pythonVersion string) { + g := DefaultGraph.(*generalGraph) + + g.UVConfig = &ir.UVConfig{ + PythonVersion: pythonVersion, + } +} + func RLang() { g := DefaultGraph.(*generalGraph) - g.Language = ir.Language{ + g.Languages = append(g.Languages, ir.Language{ Name: "r", - } + }) } func Julia() { g := DefaultGraph.(*generalGraph) - g.Language = ir.Language{ + g.Languages = append(g.Languages, ir.Language{ Name: "julia", - } + }) } func PyPIPackage(deps []string, requirementsFile string, wheels []string) error { @@ -121,6 +141,12 @@ func SystemPackage(deps []string) { g.SystemPackages = append(g.SystemPackages, deps...) } +func ShmSize(shmSize int) { + g := DefaultGraph.(*generalGraph) + + g.ShmSize = shmSize +} + func GPU(numGPUs int) { g := DefaultGraph.(*generalGraph) @@ -191,7 +217,7 @@ func JuliaPackageServer(url string) error { func Shell(shell string) error { g := DefaultGraph.(*generalGraph) - g.Shell = shell + g.Shell = strings.ToLower(shell) return nil } @@ -246,6 +272,9 @@ func CondaChannel(channel string) error { func CondaPackage(deps []string, channel []string, envFile string) error { g := DefaultGraph.(*generalGraph) + if g.CondaConfig == nil { + return errors.New("cannot install conda packages when conda is not installed") + } g.CondaConfig.CondaPackages = append( g.CondaConfig.CondaPackages, deps...) @@ -258,12 +287,13 @@ func CondaPackage(deps []string, channel []string, envFile string) error { return nil } -func Copy(src, dest string) { +func Copy(src, dest, image string) { g := DefaultGraph.(*generalGraph) g.Copy = append(g.Copy, ir.CopyInfo{ Source: src, Destination: dest, + Image: image, }) } diff --git a/pkg/lang/ir/v1/julia.go b/pkg/lang/ir/v1/julia.go index 3e3aff706..228b693fa 100644 --- a/pkg/lang/ir/v1/julia.go +++ b/pkg/lang/ir/v1/julia.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -36,7 +36,6 @@ var downloadJuliaBashScript string // getJuliaBinary returns the llb.State only after setting up Julia environment // A successful run of getJuliaBinary should set up the Julia environment func (g generalGraph) getJuliaBinary(root llb.State) llb.State { - base := llb.Image(builderImage) builder := base. Run(llb.Shlexf("sh -c '%s'", downloadJuliaBashScript), @@ -57,42 +56,34 @@ func (g generalGraph) getJuliaBinary(root llb.State) llb.State { // installJulia returns the llb.State only after adding the Julia environment to $PATH // A successful run of installJulia should add Julia to global environment path func (g *generalGraph) installJulia(root llb.State) llb.State { - confJulia := g.getJuliaBinary(root) confJulia = g.updateEnvPath(confJulia, juliaBinDir) - return confJulia } // installJuliaPackages returns the llb.State only after installing required Julia packages // A successful run of installJuliaPackages should install Julia packages under "/opt/julia/user_packages" and export the path func (g *generalGraph) installJuliaPackages(root llb.State) llb.State { - if len(g.JuliaPackages) == 0 { return root } root = root.File(llb.Mkdir(juliaPkgDir, 0755, llb.WithParents(true)), llb.WithCustomName("[internal] creating folder for julia packages")) - // Allow root to utilize the installed Julia environment root = g.updateEnvPath(root, juliaBinDir) - // Export "/opt/julia/user_packages" as the additional library path for root root = root.AddEnv("JULIA_DEPOT_PATH", juliaPkgDir) - // Export "/opt/julia/user_packages" as the additional library path for users g.RuntimeEnviron["JULIA_DEPOT_PATH"] = juliaPkgDir - // Change owner of the "/opt/julia/user_packages" to users g.UserDirectories = append(g.UserDirectories, juliaPkgDir) for _, packages := range g.JuliaPackages { command := fmt.Sprintf(`julia -e 'using Pkg; Pkg.add(["%s"])'`, strings.Join(packages, `","`)) run := root. - Run(llb.Shlex(command), llb.WithCustomNamef("[internal] installing Julia pacakges: %s", strings.Join(packages, " "))) + Run(llb.Shlex(command), llb.WithCustomNamef("[internal] installing Julia packages: %s", strings.Join(packages, " "))) root = run.Root() } - return root } diff --git a/pkg/lang/ir/v1/julia.sh b/pkg/lang/ir/v1/julia.sh index 9d5c5c71d..c5ba65cc2 100644 --- a/pkg/lang/ir/v1/julia.sh +++ b/pkg/lang/ir/v1/julia.sh @@ -1,12 +1,18 @@ set -o pipefail && \ -JULIA_URL="https://julialang-s3.julialang.org/bin/linux/x64/1.8/julia-1.8.5-linux-x86_64.tar.gz"; \ -SHA256SUM="e71a24816e8fe9d5f4807664cbbb42738f5aa9fe05397d35c81d4c5d649b9d05"; \ +UNAME_M="$(uname -m)" && \ +if [ "${UNAME_M}" = "x86_64" ]; then \ + JULIA_URL="https://julialang-s3.julialang.org/bin/linux/x64/1.10/julia-1.10.8-linux-x86_64.tar.gz"; \ + SHA256SUM="0410175aeec3df63173c15187f2083f179d40596d36fd3a57819cc5f522ae735"; \ +elif [ "{UNAME_M}" = "aarch64" ]; then \ + JULIA_URL="https://julialang-s3.julialang.org/bin/linux/aarch64/1.10/julia-1.10.8-linux-aarch64.tar.gz" \ + SHA256SUM="8d63dd12595a08edc736be8d6c4fea1840f137b81c62079d970dbd1be448b8cd"; \ +fi && \ wget "${JULIA_URL}" -O /tmp/julia.tar.gz && \ echo "${SHA256SUM} /tmp/julia.tar.gz" > /tmp/sha256sum && \ sha256sum -c -s /tmp/sha256sum EXIT_CODE=$? -if [ $EXIT_CODE -ne 0 ]; then +if [ $EXIT_CODE -ne 0 ]; then echo "CHECKSUM FAILED" && \ rm /tmp/julia.tar.gz && \ wget "${JULIA_URL}" -O /tmp/julia.tar.gz && \ @@ -14,4 +20,3 @@ if [ $EXIT_CODE -ne 0 ]; then else echo "CHECKSUM PASSED" fi - diff --git a/pkg/lang/ir/v1/pixi.go b/pkg/lang/ir/v1/pixi.go new file mode 100644 index 000000000..870e745bb --- /dev/null +++ b/pkg/lang/ir/v1/pixi.go @@ -0,0 +1,88 @@ +// Copyright 2025 The envd 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 v1 + +import ( + "bytes" + "text/template" + + "github.com/moby/buildkit/client/llb" + "github.com/sirupsen/logrus" +) + +const ( + pixiVersion = "0.48.0" + pixiConfigTemplate = ` +{{- if .UsePixiMirror -}} +[mirrors] +"https://conda.anaconda.org" = ["https://prefix.dev/"] +# prefix.dev doesn't mirror conda-forge's label channels +# pixi uses the longest matching prefix for the mirror +"https://conda.anaconda.org/conda-forge/label" = [ + "https://conda.anaconda.org/conda-forge/label", +] +{{- end -}} +{{- if .PyPIIndex -}} +[pypi-config] +index-url = "{{ .PyPIIndex }}" +{{- end -}} +` +) + +func (g generalGraph) compilePixi(root llb.State) llb.State { + if g.PixiConfig == nil { + return root + } + + base := llb.Image(builderImage) + builder := base.Run( + llb.Shlexf(`sh -c "wget -qO- https://github.com/prefix-dev/pixi/releases/download/v%s/pixi-$(uname -m)-unknown-linux-musl.tar.gz | tar -xz -C /tmp || exit 1"`, pixiVersion), + llb.WithCustomNamef("[internal] download pixi %s", pixiVersion), + ).Root() + + root = root.File( + llb.Copy(builder, "/tmp/pixi", "/usr/bin/pixi"), llb.WithCustomName("[internal] install pixi"), + ) + + return g.compilePixiConfig(root) +} + +func (g generalGraph) compilePixiConfig(root llb.State) llb.State { + if !g.PixiConfig.UsePixiMirror && g.PixiConfig.PyPIIndex == nil { + return root + } + + root = root.File( + llb.Mkdir("/etc/pixi", 0777, llb.WithParents(true)), + llb.WithCustomName("[internal] create pixi config directory"), + ) + + tmpl, err := template.New("pixi-config").Parse(pixiConfigTemplate) + if err != nil { + logrus.Errorf("failed to parse pixi config template: %v", err) + return root + } + var buf bytes.Buffer + err = tmpl.Execute(&buf, g.PixiConfig) + if err != nil { + logrus.Errorf("failed to execute pixi config template: %v", err) + return root + } + root = root.File( + llb.Mkfile("/etc/pixi/config.toml", 0755, buf.Bytes(), llb.WithUIDGID(g.uid, g.gid)), + llb.WithCustomName("[internal] create pixi config file"), + ) + return root +} diff --git a/pkg/lang/ir/v1/python.go b/pkg/lang/ir/v1/python.go index 3a7df1b8b..ef53f73e4 100644 --- a/pkg/lang/ir/v1/python.go +++ b/pkg/lang/ir/v1/python.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -29,7 +29,7 @@ import ( ) const ( - PythonVersionDefault = "3.9" + PythonVersionDefault = "3.11" microMambaPathPrefix = "/usr/local/bin" certPath = "/etc/ssl/certs" ) @@ -68,11 +68,16 @@ func (g *generalGraph) installPython(root llb.State) (llb.State, error) { } func (g generalGraph) getAppropriatePythonVersion() (string, error) { - if g.Language.Version == nil { - return PythonVersionDefault, nil + var version string + for _, language := range g.Languages { + if language.Name == "python" { + if language.Version == nil { + return PythonVersionDefault, nil + } + version = *language.Version + } } - version := *g.Language.Version if version == "3" || version == "" { return PythonVersionDefault, nil } @@ -86,14 +91,13 @@ func (g generalGraph) getAppropriatePythonVersion() (string, error) { func (g generalGraph) compileAlternative(root llb.State) llb.State { envdPrefix := "/opt/conda/envs/envd/bin" run := root. - Run(llb.Shlexf("update-alternatives --install /usr/bin/python python %s/python 1", envdPrefix), - llb.WithCustomName("[internal] update alternative python to envd")). - Run(llb.Shlexf("update-alternatives --install /usr/bin/python3 python3 %s/python3 1", envdPrefix), - llb.WithCustomName("[internal] update alternative python3 to envd")). - Run(llb.Shlexf("update-alternatives --install /usr/bin/pip pip %s/pip 1", envdPrefix), - llb.WithCustomName("[internal] update alternative pip to envd")). - Run(llb.Shlexf("update-alternatives --install /usr/bin/pip3 pip3 %s/pip3 1", envdPrefix), - llb.WithCustomName("[internal] update alternative pip3 to envd")) + Run(llb.Shlexf(`sh -c " + update-alternatives --install /usr/bin/python python %[1]s/python 1 && + update-alternatives --install /usr/bin/python3 python3 %[1]s/python3 1 && + update-alternatives --install /usr/bin/pip pip %[1]s/pip 1 && + update-alternatives --install /usr/bin/pip3 pip3 %[1]s/pip3 1 + "`, envdPrefix), + llb.WithCustomName("[internal] update alternative python/python3/pip/pip3 to envd")) return run.Root() } @@ -126,15 +130,25 @@ func (g generalGraph) compilePyPIPackages(root llb.State) llb.State { if g.RequirementsFile != nil { logrus.WithField("file", *g.RequirementsFile). Debug("Configure pip install requirements statements") - root = root.Dir(g.getWorkingDir()) - run := root. - Run(llb.Shlexf("python -m pip install -r %s", *g.RequirementsFile), - llb.WithCustomNamef("pip install -r %s", *g.RequirementsFile)) - run.AddMount(cacheDir, cache, - llb.AsPersistentCacheDir(g.CacheID(cacheDir), llb.CacheMountShared), llb.SourcePath("/cache/pip")) - run.AddMount(g.getWorkingDir(), - llb.Local(flag.FlagBuildContext)) - root = run.Root() + dependencies, safeToCopy := g.IsRequirementsFileSafeToCopyContent() + logrus.WithField("safeToCopy", safeToCopy).WithField("dependencies", dependencies). + Debug("Is requirements file safe to copy") + if safeToCopy { + // avoid mounting host directory to make it cache friendly + root = root.Run(llb.Shlexf( + "python -m pip install %s", + strings.Join(dependencies, " ")), + llb.WithCustomNamef("[internal] pip install from %s with %s", *g.RequirementsFile, strings.Join(dependencies, " ")), + ).Root() + } else { + run := root.Dir(g.getWorkingDir()). + Run(llb.Shlexf("python -m pip install -r %s", *g.RequirementsFile), + llb.WithCustomNamef("[internal] pip install -r %s", *g.RequirementsFile)) + run.AddMount(cacheDir, cache, + llb.AsPersistentCacheDir(g.CacheID(cacheDir), llb.CacheMountShared), llb.SourcePath("/cache/pip")) + run.AddMount(g.getWorkingDir(), llb.Local(flag.FlagBuildContext)) + root = run.Root() + } } if len(g.PythonWheels) > 0 { diff --git a/pkg/lang/ir/v1/r.go b/pkg/lang/ir/v1/r.go index 223631bcd..9f82de269 100644 --- a/pkg/lang/ir/v1/r.go +++ b/pkg/lang/ir/v1/r.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -21,17 +21,21 @@ import ( "github.com/moby/buildkit/client/llb" ) -func (g generalGraph) installRLang(root llb.State) llb.State { +const rPath = "/usr/local/lib/R/site-library" - installR := "apt-get update && apt-get install -y -t focal-cran40 r-base" - - run := root.Run(llb.Shlexf("bash -c \"%s\"", installR), +func (g *generalGraph) installRLang(root llb.State) llb.State { + g.UserDirectories = append(g.UserDirectories, rPath) + prepare := root.Run(llb.Shlex(`sh -c " +wget -qO- https://cloud.r-project.org/bin/linux/ubuntu/marutter_pubkey.asc | gpg --dearmor -o /usr/share/keyrings/r-project.gpg && +echo "deb [signed-by=/usr/share/keyrings/r-project.gpg] https://cloud.r-project.org/bin/linux/ubuntu jammy-cran40/" | tee -a /etc/apt/sources.list.d/r-project.list +"`), llb.WithCustomName("add R public GPG key")).Root() + run := prepare.Run( + llb.Shlex(`sh -c "apt-get update && apt-get install -y --no-install-recommends r-base"`), llb.WithCustomNamef("[internal] apt install R environment from CRAN repository")) return run.Root() } func (g generalGraph) installRPackages(root llb.State) llb.State { - if len(g.RPackages) == 0 { return root } @@ -41,18 +45,11 @@ func (g generalGraph) installRPackages(root llb.State) llb.State { mirrorURL = *g.CRANMirrorURL } - lib := "/usr/local/lib/R/site-library/" - - root = root. - Run(llb.Shlexf("chmod 777 %s", lib), llb.WithCustomNamef("[internal] setting execute permision for default R package library for envd users")).Root() - for _, packages := range g.RPackages { - command := fmt.Sprintf(`R -e 'options(repos = "%s"); install.packages(c("%s"), lib = "%s")'`, mirrorURL, strings.Join(packages, `","`), lib) + command := fmt.Sprintf(`R -e 'options(repos = "%s"); install.packages(c("%s"), lib = "%s")'`, mirrorURL, strings.Join(packages, `","`), rPath) run := root. - Run(llb.Shlex(command), llb.WithCustomNamef("[internal] installing R pacakges: %s", strings.Join(packages, " "))) + Run(llb.Shlex(command), llb.WithCustomNamef("[internal] installing R packages: %s", strings.Join(packages, " "))) root = run.Root() - } - return root } diff --git a/pkg/lang/ir/v1/shell.go b/pkg/lang/ir/v1/shell.go index b5bf5cc3d..02ddb54d0 100644 --- a/pkg/lang/ir/v1/shell.go +++ b/pkg/lang/ir/v1/shell.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ package v1 import ( + "fmt" + "github.com/cockroachdb/errors" "github.com/moby/buildkit/client/llb" @@ -53,36 +55,75 @@ diverged = "<>" renamed = "r" deleted = "x" ` + + fishVersion = "4.0.1" + fishAssetURL = "https://github.com/fish-shell/fish-shell/releases/download/%[1]s/fish-static-$(uname -m)-%[1]s.tar.xz" ) func (g *generalGraph) compileShell(root llb.State) (_ llb.State, err error) { - g.RuntimeEnviron["SHELL"] = "/usr/bin/bash" - if g.Shell == shellZSH { + g.RuntimeEnviron["SHELL"] = "bash" + switch g.Shell { + case shellZSH: g.RuntimeEnviron["SHELL"] = "/usr/bin/zsh" - root, err = g.compileZSH(root) - if err != nil { + if root, err = g.compileZSH(root); err != nil { return llb.State{}, err } + case shellFish: + g.RuntimeEnviron["SHELL"] = "/usr/bin/fish" + root = g.compileFish(root) } - if g.CondaConfig != nil { - root = g.compileCondaShell(root) - } + root = g.compileCondaShell(root) + root = g.compilePixiShell(root) return root, nil } +func (g generalGraph) compilePixiShell(root llb.State) llb.State { + if g.PixiConfig == nil { + return root + } + + switch g.Shell { + case shellZSH: + root = root.Run( + llb.Shlex(`sh -c 'echo "eval \"\$(pixi completion --shell zsh)\"" >> ~/.zshrc'`), + llb.WithCustomName("[internal] setting pixi zsh config"), + ).Root() + case shellFish: + root = root.Run( + llb.Shlex(`sh -c 'echo "pixi completion --shell fish | source" >> ~/.config/fish/config.fish'`), + llb.WithCustomName("[internal] setting pixi fish config"), + ).Root() + case shellBASH: + root = root.Run( + llb.Shlex(`sh -c 'echo "eval \"\$(pixi completion --shell bash)\"" >> ~/.bashrc'`), + llb.WithCustomName("[internal] setting pixi bash config"), + ).Root() + } + return root +} + func (g *generalGraph) compileCondaShell(root llb.State) llb.State { + if g.CondaConfig == nil { + return root + } + findDir := fileutil.DefaultHomeDir if g.Dev { findDir = fileutil.EnvdHomeDir } rcPath := findDir(".bashrc") - if g.Shell == shellZSH { + activateFile := "activate" + switch g.Shell { + case shellZSH: rcPath = findDir(".zshrc") + case shellFish: + rcPath = findDir(".config/fish/config.fish") + activateFile = "activate.fish" } run := root. Run(llb.Shlexf("bash -c \"%s\"", g.condaInitShell(g.Shell)), llb.WithCustomNamef("[internal] init conda %s env", g.Shell)). - Run(llb.Shlexf(`bash -c 'echo "source %s/activate envd" >> %s'`, condaBinDir, rcPath), + Run(llb.Shlexf(`bash -c 'echo "source %s/%s envd" >> %s'`, condaBinDir, activateFile, rcPath), llb.WithCustomNamef("[internal] add conda environment to %s", rcPath)) return run.Root() } @@ -98,10 +139,15 @@ func (g *generalGraph) compilePrompt(root llb.State) llb.State { run := config.Run(llb.Shlexf(`bash -c 'echo "eval \"\$(starship init bash)\"" >> %s'`, fileutil.EnvdHomeDir(".bashrc")), llb.WithCustomName("[internal] setting prompt bash config")).Root() - if g.Shell == shellZSH { + switch g.Shell { + case shellZSH: run = run.Run( llb.Shlexf(`bash -c 'echo "eval \"\$(starship init zsh)\"" >> %s'`, fileutil.EnvdHomeDir(".zshrc")), llb.WithCustomName("[internal] setting prompt zsh config")).Root() + case shellFish: + run = run.Run( + llb.Shlexf(`bash -c 'echo "starship init fish | source" >> %s'`, fileutil.EnvdHomeDir(".config/fish/config.fish")), + llb.WithCustomName("[internal] setting prompt fish config")).Root() } return run } @@ -112,11 +158,11 @@ func (g generalGraph) compileZSH(root llb.State) (llb.State, error) { ohMyZSHPath := fileutil.EnvdHomeDir(".oh-my-zsh") m := shell.NewManager() g.Writer.LogZSH(compileui.ActionStart, false) - if cached, err := m.DownloadOrCache(); err != nil { + cached, err := m.DownloadOrCache() + if err != nil { return llb.State{}, errors.Wrap(err, "failed to download oh-my-zsh") - } else { - g.Writer.LogZSH(compileui.ActionEnd, cached) } + g.Writer.LogZSH(compileui.ActionEnd, cached) zshStage := root. File(llb.Copy(llb.Local(flag.FlagCacheDir), "oh-my-zsh", ohMyZSHPath, &llb.CopyInfo{CreateDestPath: true})). @@ -126,3 +172,19 @@ func (g generalGraph) compileZSH(root llb.State) (llb.State, error) { File(llb.Mkfile(zshrcPath, 0666, []byte(m.ZSHRC()))) return zshrc, nil } + +func (g generalGraph) compileFish(root llb.State) llb.State { + base := llb.Image(builderImage) + url := fmt.Sprintf(fishAssetURL, fishVersion) + builder := base.Run( + llb.Shlexf(`sh -c "wget -qO- %s | tar -xJf - -C /tmp || exit 1"`, url), + llb.WithCustomName("[internal] download fish shell"), + ).Root() + root = root.File( + llb.Copy(builder, "/tmp/fish", "/usr/bin/fish"), + llb.WithCustomName("[internal] copy fish shell from the builder image")). + Run(llb.Shlex(`sh -c "echo yes | fish --install"`), + llb.WithCustomName("[internal] install fish shell")).Root() + + return root +} diff --git a/pkg/lang/ir/v1/supervisor.go b/pkg/lang/ir/v1/supervisor.go index 1ce7d87f6..1557584fc 100644 --- a/pkg/lang/ir/v1/supervisor.go +++ b/pkg/lang/ir/v1/supervisor.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -58,8 +58,11 @@ func (g generalGraph) installHorust(root llb.State) llb.State { File(llb.Mkdir(types.HorustServiceDir, 0755, llb.WithParents(true)), llb.WithCustomNamef("[internal] mkdir for horust service: %s", types.HorustServiceDir)). File(llb.Mkdir(types.HorustLogDir, 0777, llb.WithParents(true)), - llb.WithCustomNamef("[internal] mkdir for horust log: %s", types.HorustLogDir)) - return horust + llb.WithCustomNamef("[internal] mkdir for horust log: %s", types.HorustLogDir)). + Run(llb.Shlexf(`sudo chmod 777 %s`, types.HorustLogDir), + llb.WithCustomName("[internal] change directory permission for logging")) + + return horust.Root() } func (g generalGraph) addNewProcess(root llb.State, name, command string, depends []string) llb.State { diff --git a/pkg/lang/ir/v1/system.go b/pkg/lang/ir/v1/system.go index 12488569d..1f7b651c0 100644 --- a/pkg/lang/ir/v1/system.go +++ b/pkg/lang/ir/v1/system.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -24,7 +24,6 @@ import ( "github.com/cockroachdb/errors" "github.com/moby/buildkit/client/llb" - "github.com/moby/buildkit/client/llb/imagemetaresolver" "github.com/sirupsen/logrus" "github.com/tensorchord/envd/pkg/config" @@ -34,13 +33,6 @@ import ( "github.com/tensorchord/envd/pkg/util/fileutil" ) -// signFolder stores the path to the apt-source signature in string format -// The value of signFolder should always be /etc/apt/keyrings -const signFolder = "/etc/apt/keyrings" - -// signURI stores the third-party URI for downloading the signature of the corresponding repo -const signURI = "https://cloud.r-project.org/bin/linux/ubuntu/marutter_pubkey.asc" - func (g generalGraph) compileUbuntuAPT(root llb.State) llb.State { if g.UbuntuAPTSource != nil { logrus.WithField("source", *g.UbuntuAPTSource).Debug("using custom APT source") @@ -54,89 +46,6 @@ func (g generalGraph) compileUbuntuAPT(root llb.State) llb.State { return root } -// copyAPTSignature returns the state and the path in string format -// The returned string represents the location of the apt-source signature -// A successful run of copyAPTSignature should return path of /etc/apt/keyrings/*.asc -func (g generalGraph) copyAPTSignature(root llb.State, name string, url string) (llb.State, string) { - - var fileName = fmt.Sprintf("%s.asc", name) - - // path stores the location of the signature file - // The value of path should be /etc/apt/keyrings/*.asc - var path = filepath.Join(signFolder, fileName) - - base := llb.Image(builderImage) - builder := base. - Run(llb.Shlexf("sh -c \"curl %s >> %s\"", url, fileName), - llb.WithCustomName("[internal] downloading apt-source signature in base image")).Root() - - aptSign := root. - File(llb.Mkdir(signFolder, 0755, llb.WithParents(true)), - llb.WithCustomName("[internal] setting target apt-source signature folder")). - File(llb.Copy(builder, fileName, path), - llb.WithCustomName("[internal] copy signature from builder")) - - return aptSign, path -} - -// configRSrc returns the state and the content in DEB822 format for third-party apt-source -// The returned string contains all configuration for adding third-party into apt source list -// A successful run of configRSrc should return the content strictly follow the DEB822 format -func (g generalGraph) configRSrc(root llb.State, aptConfig ir.APTConfig, sign string) (llb.State, string) { - - var enabled = fmt.Sprintf("Enabled: %s\n", aptConfig.Enabled) - var types = fmt.Sprintf("Types: %s\n", aptConfig.Types) - var uris = fmt.Sprintf("URIs: %s\n", aptConfig.URIs) - var suites = fmt.Sprintf("Suites: %s\n", aptConfig.Suites) - var components = fmt.Sprintf("Components: %s\n", aptConfig.Components) - var architecture = fmt.Sprintf("Architectures: %s\n", aptConfig.Arch) - - aptSign, signPath := g.copyAPTSignature(root, aptConfig.Name, sign) - var signature = fmt.Sprintf("Signed-By: %s\n", signPath) - - var content strings.Builder - content.WriteString(enabled) - content.WriteString(types) - content.WriteString(uris) - content.WriteString(suites) - content.WriteString(components) - content.WriteString(signature) - content.WriteString(architecture) - - return aptSign, content.String() -} - -// compileRLang returns the llb.State only after compoiling the environment for installing R language -// A successful run of compileRLang should set up the official R apt repo into /etc/apt/sources.list.d/*.sources -func (g generalGraph) compileRLang(root llb.State) llb.State { - - var aptConfig = ir.APTConfig{ - Name: "R-base", // Name for the *.sources file in /etc/apt/sources.list.d - Enabled: "yes", // Represents the validation of the third-party repo - Types: "deb", // Type of the repo, binary or source code - URIs: "https://cloud.r-project.org/bin/linux/ubuntu", // URI repo for the package - Suites: "focal-cran40/", // Branch of the package - Components: "", // Distribution of the package. E.g. main, non-free - Signed: "R-base.asc", // Name for the signature file - Arch: "", // Architecture that is supported - } - - var file = fmt.Sprintf("/etc/apt/sources.list.d/%s.sources", aptConfig.Name) - - aptSource, content := g.configRSrc(root, aptConfig, signURI) - - aptRLang := aptSource. - File(llb.Mkdir("/etc/apt/sources.list.d/", 0755, llb.WithParents(true)), - llb.WithCustomName("[internal] setting apt-source folder sources.list.d")). - File(llb.Mkfile(file, 0644, []byte(content)), - llb.WithCustomName("[internal] setting apt-source file")). - File(llb.Mkfile("/etc/apt/apt.conf.d/DEB822.conf", 0644, []byte("APT::Sources::Use-Deb822 true;\n")), - llb.WithCustomName("[internal] setting apt-conf file to support DEB822 format")) - - return aptRLang - -} - func (g generalGraph) compileRun(root llb.State) llb.State { if len(g.Exec) == 0 { return root @@ -150,12 +59,9 @@ func (g generalGraph) compileRun(root llb.State) llb.State { sb.WriteString(c + "\n") } - cmdStr := fmt.Sprintf("/usr/bin/bash -c '%s'", sb.String()) + cmdStr := fmt.Sprintf("bash -c '%s'", sb.String()) logrus.WithField("command", cmdStr).Debug("compile run command") - // Mount the build context into the build process. - // TODO(gaocegege): Maybe we should make it readonly, - // but these cases then cannot be supported: - // run(commands=["git clone xx.git"]) + // mount host here is read-only run := root.Dir(workingDir).Run(llb.Shlex(cmdStr)) if execGroup.MountHost { run.AddMount(workingDir, llb.Local(flag.FlagBuildContext)) @@ -173,8 +79,15 @@ func (g generalGraph) compileCopy(root llb.State) llb.State { result := root // Compose the copy command. for _, c := range g.Copy { + var from llb.State + if c.Image == "" { + from = llb.Local(flag.FlagBuildContext) + } else { + from = llb.Image(c.Image) + } result = result.File(llb.Copy( - llb.Local(flag.FlagBuildContext), c.Source, c.Destination, + from, c.Source, c.Destination, + &llb.CopyInfo{CreateDestPath: true}, llb.WithUIDGID(g.uid, g.gid))) } return result @@ -208,9 +121,23 @@ func (g generalGraph) compileSystemPackages(root llb.State) llb.State { } // nolint:unparam -func (g *generalGraph) compileExtraSource(root llb.State) (llb.State, error) { +func (g *generalGraph) compileExtraSource(root llb.State) llb.State { if len(g.HTTP) == 0 { - return root, nil + return root + } + if g.DisableMergeOp { + for _, httpInfo := range g.HTTP { + src := llb.HTTP( + httpInfo.URL, + llb.Checksum(httpInfo.Checksum), + llb.Filename(httpInfo.Filename), + llb.Chown(g.uid, g.gid), + ) + root = root.File(llb.Copy( + src, "/", g.getExtraSourceDir(), &llb.CopyInfo{CreateDestPath: true}, + )) + } + return root } inputs := []llb.State{} for _, httpInfo := range g.HTTP { @@ -225,64 +152,83 @@ func (g *generalGraph) compileExtraSource(root llb.State) (llb.State, error) { )) } inputs = append(inputs, root) - return llb.Merge(inputs, llb.WithCustomName("[internal] build source layers")), nil + return llb.Merge(inputs, llb.WithCustomName("[internal] build source layers")) } func (g *generalGraph) compileLanguage(root llb.State) (llb.State, error) { + langs := []llb.State{} lang := root var err error - switch g.Language.Name { - case "python": - lang, err = g.installPython(root) - case "r": - rSrc := g.compileRLang(root) - lang = g.installRLang(rSrc) - case "julia": - lang = g.installJulia(root) + + if g.DisableMergeOp { + for _, language := range g.Languages { + switch language.Name { + case "python": + root, err = g.installPython(root) + case "r": + root = g.installRLang(root) + case "julia": + root = g.installJulia(root) + } + } + return root, err } - return lang, err + for _, language := range g.Languages { + switch language.Name { + case "python": + lang, err = g.installPython(root) + case "r": + lang = g.installRLang(root) + case "julia": + lang = g.installJulia(root) + } + langs = append(langs, lang) + } + if len(langs) <= 1 { + return lang, err + } + for i, lang := range g.Languages { + langs[i] = llb.Diff(root, langs[i], llb.WithCustomNamef("[internal] build %s env", lang.Name)) + } + return llb.Merge(append([]llb.State{root}, langs...), + llb.WithCustomName("[internal] merge all the language environments")), err } func (g *generalGraph) compileLanguagePackages(root llb.State) llb.State { - pack := root - switch g.Language.Name { - case "python": - index := g.compilePyPIIndex(root) - pypi := g.compilePyPIPackages(index) - if g.CondaConfig == nil { - pack = pypi - } else { - channel := g.compileCondaChannel(root) - conda := g.compileCondaPackages(channel) - pack = llb.Merge([]llb.State{ - root, - llb.Diff(root, pypi, llb.WithCustomName("[internal] PyPI packages")), - llb.Diff(root, conda, llb.WithCustomName("[internal] conda packages")), - }, llb.WithCustomName("[internal] Python packages")) + // Use default python in the base image if install.python() is not specified. + g.compileJupyter() + index := g.compilePyPIIndex(root) + pack := g.compilePyPIPackages(index) + if g.CondaConfig != nil { + channel := g.compileCondaChannel(pack) + pack = g.compileCondaPackages(channel) + } + if g.UVConfig != nil { + pack = g.compileUV(pack) + } + if g.PixiConfig != nil { + pack = g.compilePixi(pack) + } + + for _, language := range g.Languages { + switch language.Name { + case "r": + pack = g.installRPackages(pack) + case "julia": + pack = g.installJuliaPackages(pack) } - case "r": - pack = g.installRPackages(root) - case "julia": - pack = g.installJuliaPackages(root) } return pack } func (g *generalGraph) compileDevPackages(root llb.State) llb.State { - for _, env := range types.BaseEnvironment { - root = root.AddEnv(env.Name, env.Value) - } - // apt packages var sb strings.Builder sb.WriteString("apt-get update && apt-get install -y apt-utils && ") sb.WriteString("apt-get install -y --no-install-recommends --no-install-suggests --fix-missing ") sb.WriteString(strings.Join(types.BaseAptPackage, " ")) - sb.WriteString("&& rm -rf /var/lib/apt/lists/* ") - // shell prompt - sb.WriteString("&& curl --proto '=https' --tlsv1.2 -sSf https://starship.rs/install.sh | sh -s -- -y") - sb.WriteString("&& locale-gen en_US.UTF-8") + sb.WriteString("&& rm -rf /var/lib/apt/lists/*") run := root.Run(llb.Shlexf(`bash -c "%s"`, sb.String()), llb.WithCustomName("[internal] install built-in packages")) @@ -290,6 +236,14 @@ func (g *generalGraph) compileDevPackages(root llb.State) llb.State { return run.Root() } +func (g generalGraph) compileStarship(root llb.State) llb.State { + starship := root.File(llb.Copy( + llb.Image(types.EnvdStarshipImage), "/usr/local/bin/starship", "/usr/local/bin/starship", + &llb.CopyInfo{CreateDestPath: true}), + llb.WithCustomName(fmt.Sprintf("[internal] add envd-starship from %s", types.EnvdStarshipImage))) + return starship +} + func (g generalGraph) compileSSHD(root llb.State) llb.State { sshd := root.File(llb.Copy( llb.Image(types.EnvdSshdImage), "/usr/bin/envd-sshd", "/var/envd/bin/envd-sshd", @@ -306,29 +260,70 @@ func (g *generalGraph) compileBaseImage() (llb.State, error) { logger := logrus.WithFields(logrus.Fields{ "image": g.Image, - "language": g.Language.Name, + "language": g.Languages, }) - if g.Language.Version != nil { - logger = logger.WithField("version", *g.Language.Version) - } logger.Debug("compile base image") // Fix https://github.com/tensorchord/envd/issues/1147. // Fetch the image metadata from base image. - base := llb.Image(g.Image, llb.WithMetaResolver(imagemetaresolver.Default())) - envs, err := base.Env(context.Background()) + base := llb.Image(g.Image) + // fetching the image config may take some time + config, err := ir.FetchImageConfig(context.Background(), g.Image, g.Platform) if err != nil { - return llb.State{}, errors.Wrap(err, "failed to get the image metadata") + return llb.State{}, errors.Wrapf(err, "failed to get the image config, check if the image(%s) exists", g.Image) } // Set the environment variables to RuntimeEnviron to keep it in the resulting image. - for _, e := range envs { + logger.Logger.Debugf("inherit envs from base image: %s", config.Env) + for _, e := range config.Env { // in case the env value also contains `=` kv := strings.SplitN(e, "=", 2) g.RuntimeEnviron[kv[0]] = kv[1] + if kv[0] == "PATH" { + // deduplicate the PATH but keep the order as: + // 0. default Unix PATH + // 1. configured paths in the Starlark frontend `runtime.environ(extra_path=[...])` + // 2. paths in the base image + // 3. others added during the image building (Python paths, etc.) + + // iterate over the original paths and add them to the map + pathMap := make(map[string]bool) + for _, path := range g.RuntimeEnvPaths { + pathMap[path] = true + } + // split the PATH into different paths + newPaths := strings.Split(kv[1], ":") + // iterate over the new paths + for _, path := range newPaths { + // check if the path is already in the map + if _, ok := pathMap[path]; !ok { + // if not, add the path to the map and slice + pathMap[path] = true + g.RuntimeEnvPaths = append(g.RuntimeEnvPaths, path) + } + } + } + } + + // add necessary envs + for _, env := range types.BaseEnvironment { + base = base.AddEnv(env.Name, env.Value) + } + for k, v := range g.RuntimeEnviron { + base = base.AddEnv(k, v) + } + + if !g.Dev { + if len(g.Entrypoint) == 0 { + g.Entrypoint = config.Entrypoint + } + g.User = config.User + g.WorkingDir = config.WorkingDir + } else { + // for dev mode, we will create an `envd` user + g.User = "" + g.WorkingDir = g.getWorkingDir() } - // TODO: inherit the USER from base - g.User = "" return base, nil } diff --git a/pkg/lang/ir/v1/types.go b/pkg/lang/ir/v1/types.go index d74691476..5e84f055f 100644 --- a/pkg/lang/ir/v1/types.go +++ b/pkg/lang/ir/v1/types.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ package v1 import ( + ocispecs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/tensorchord/envd/pkg/editor/vscode" "github.com/tensorchord/envd/pkg/lang/ir" "github.com/tensorchord/envd/pkg/progress/compileui" @@ -28,15 +30,17 @@ type generalGraph struct { uid int `default:"-1"` gid int `default:"-1"` - ir.Language - Image string - User string + Languages []ir.Language + EnvdSyntaxVersion string + Image string + User string Shell string Dev bool CUDA *string CUDNN string NumGPUs int + ShmSize int UbuntuAPTSource *string CRANMirrorURL *string @@ -68,18 +72,32 @@ type generalGraph struct { *ir.JupyterConfig *ir.GitConfig *ir.CondaConfig + *ir.UVConfig + *ir.PixiConfig *ir.RStudioServerConfig - Writer compileui.Writer + Writer compileui.Writer `json:"-"` // EnvironmentName is the base name of the environment. // It is the BaseDir(BuildContextDir) // e.g. mnist, streamlit-mnist EnvironmentName string + // EnvironmentPath is the full path of this environment. + EnvironmentPath string + // WorkingDir is the working directory of this environment. + // This only affect the `WorkingDir` in the image config. + WorkingDir string + + // (v1) disable `merge` op for `moby` builder + // check https://github.com/tensorchord/envd/issues/1693 + DisableMergeOp bool ir.RuntimeGraph + + Platform *ocispecs.Platform } const ( shellBASH = "bash" shellZSH = "zsh" + shellFish = "fish" ) diff --git a/pkg/lang/ir/v1/user.go b/pkg/lang/ir/v1/user.go index 680b79e09..0090b1d58 100644 --- a/pkg/lang/ir/v1/user.go +++ b/pkg/lang/ir/v1/user.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -39,6 +39,9 @@ func (g *generalGraph) compileUserOwn(root llb.State) llb.State { for _, env := range types.BaseEnvironment { user = user.AddEnv(env.Name, env.Value) } + for k, v := range g.RuntimeEnviron { + user = user.AddEnv(k, v) + } user = user.AddEnv("PATH", strings.Join(g.RuntimeEnvPaths, ":")) return user } diff --git a/pkg/lang/ir/v1/util.go b/pkg/lang/ir/v1/util.go index 5260ac30a..d32547ee4 100644 --- a/pkg/lang/ir/v1/util.go +++ b/pkg/lang/ir/v1/util.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,12 +15,16 @@ package v1 import ( + "bufio" "bytes" "crypto/md5" "encoding/gob" "encoding/hex" + "encoding/json" "fmt" + "os" "os/user" + "path/filepath" "regexp" "runtime" "strconv" @@ -31,6 +35,7 @@ import ( "github.com/spf13/viper" "github.com/tensorchord/envd/pkg/flag" + "github.com/tensorchord/envd/pkg/lang/ir" "github.com/tensorchord/envd/pkg/util/fileutil" ) @@ -134,12 +139,86 @@ func GetDefaultGraphHash() string { // GetCUDAImage finds the correct CUDA base image // refer to https://hub.docker.com/r/nvidia/cuda/tags func GetCUDAImage(image string, cuda *string, cudnn string, dev bool) string { - // TODO: support CUDA 10 target := "runtime" if dev { target = "devel" } imageTag := strings.Replace(image, ":", "", 1) - return fmt.Sprintf("docker.io/nvidia/cuda:%s-cudnn%s-%s-%s", *cuda, cudnn, target, imageTag) + newImage := fmt.Sprintf("docker.io/nvidia/cuda:%s-cudnn%s-%s-%s", *cuda, cudnn, target, imageTag) + if image != defaultImage { + logrus.Warnf("CUDA is only tested for %s, it might work with Ubuntu 18/22 base images. This feature will replace your base image to %s, make sure this meets your expectation.", defaultImage, newImage) + } + return newImage +} + +func (g *generalGraph) Dump() (string, error) { + b, err := json.Marshal(g) + if err != nil { + return "", err + } + runtimeGraphCode := string(b) + return runtimeGraphCode, nil +} + +func (g *generalGraph) Load(code []byte) error { + err := json.Unmarshal(code, g) + if err != nil { + return errors.Wrap(err, "failed to unmarshal") + } + return nil +} + +func (g generalGraph) GeneralGraphFromLabel(label []byte) (ir.Graph, error) { + newg := generalGraph{} + err := newg.Load(label) + if err != nil { + return nil, err + } + return &newg, nil +} + +// IsRequirementsFileSafeToCopyContent returns true and the dependencies in this file if the +// requirements file is safe to copy. Otherwise, it returns false and empty dependencies. +// check https://github.com/tensorchord/envd/issues/1629 +func (g generalGraph) IsRequirementsFileSafeToCopyContent() (dependencies []string, safe bool) { + if g.RequirementsFile == nil { + return + } + filePath := filepath.Join(g.EnvironmentPath, *g.RequirementsFile) + file, err := os.Open(filePath) + if err != nil { + logrus.WithError(err).Debugf("failed to open requirements file: %s", filePath) + return + } + defer file.Close() + + forbidden := []string{ + "-e", + "-r", + "-c", + "--find-links", + } + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, "#") { + continue + } + for _, f := range forbidden { + if strings.Contains(line, f) { + logrus.Debugf("requirements file contains %s, which is not safe to copy", f) + return []string{}, false + } + } + exist, err := fileutil.FileExists(line) + // has a local file to install + if err == nil && exist { + logrus.Debugf("requirements file contains local file %s, which is not safe to copy", line) + return []string{}, false + } + dependencies = append(dependencies, line) + } + return dependencies, true } diff --git a/pkg/lang/ir/v1/util_test.go b/pkg/lang/ir/v1/util_test.go index ee1bb8b3f..2234ea27f 100644 --- a/pkg/lang/ir/v1/util_test.go +++ b/pkg/lang/ir/v1/util_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/lang/ir/v1/uv.go b/pkg/lang/ir/v1/uv.go new file mode 100644 index 000000000..3d3bd4834 --- /dev/null +++ b/pkg/lang/ir/v1/uv.go @@ -0,0 +1,53 @@ +// Copyright 2025 The envd 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 v1 + +import "github.com/moby/buildkit/client/llb" + +const ( + uvVersion = "0.7.10" +) + +func (g generalGraph) compileUV(root llb.State) llb.State { + if g.UVConfig == nil { + return root + } + // uv configurations + g.RuntimeEnviron["UV_LINK_MODE"] = "copy" + g.RuntimeEnviron["UV_PYTHON_PREFERENCE"] = "only-managed" + + base := llb.Image(builderImage) + builder := base.Run( + llb.Shlexf(`sh -c "wget -qO- https://github.com/astral-sh/uv/releases/download/%s/uv-$(uname -m)-unknown-linux-gnu.tar.gz | tar -xz --strip-components=1 -C /tmp || exit 1"`, uvVersion), + llb.WithCustomNamef("[internal] download uv %s", uvVersion), + ).Root() + + root = root.File( + llb.Copy(builder, "/tmp/uv", "/usr/bin/uv"), llb.WithCustomName("[internal] install uv")). + File(llb.Copy(builder, "/tmp/uvx", "/usr/bin/uvx"), llb.WithCustomName("[internal] install uvx")) + return g.compileUVPython(root) +} + +func (g generalGraph) compileUVPython(root llb.State) llb.State { + if g.UVConfig == nil { + return root + } + + root = root.Run( + llb.Shlexf(`uv python install %s`, g.UVConfig.PythonVersion), + llb.WithCustomNamef("[internal] install uv Python=%s", g.UVConfig.PythonVersion), + ).Root() + return root +} diff --git a/pkg/lang/version/version.go b/pkg/lang/version/version.go index 6dba93803..3818ae68f 100644 --- a/pkg/lang/version/version.go +++ b/pkg/lang/version/version.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -23,10 +23,8 @@ import ( "github.com/sirupsen/logrus" "github.com/tensorchord/envd/pkg/lang/frontend/starlark" - starlarkv0 "github.com/tensorchord/envd/pkg/lang/frontend/starlark/v0" starlarkv1 "github.com/tensorchord/envd/pkg/lang/frontend/starlark/v1" "github.com/tensorchord/envd/pkg/lang/ir" - v0 "github.com/tensorchord/envd/pkg/lang/ir/v0" v1 "github.com/tensorchord/envd/pkg/lang/ir/v1" ) @@ -42,11 +40,11 @@ type Getter interface { type Version string const ( + // V0 is no longer supported in envd v1. + V0 Version = "v0" // V1 is the v1 version of the starlark frontend language. + // V1 is the default version. V1 Version = "v1" - // V0 is the v0 version of the starlark frontend language. - // v0 is the default version of the language. - V0 Version = "v0" // VersionUnknown is the unknown version of the starlark frontend language. VersionUnknown Version = "unknown" ) @@ -55,6 +53,20 @@ type generalGetter struct { v Version } +func NewByVersion(ver string) Getter { + g := &generalGetter{} + switch ver { + case string(V1): + logrus.Debug("explicit using v1") + case string(V0): + logrus.Fatal("v0 is no longer supported in envd v1, try to use v1") + default: + logrus.Debug("unknown version, using v1 by default") + } + g.v = V1 + return g +} + // New returns a new version getter. func New(file string) (Getter, error) { f, err := os.Open(file) @@ -69,13 +81,13 @@ func New(file string) (Getter, error) { g := &generalGetter{} if strings.Contains(comment, "# syntax=v1") { - g.v = V1 + logrus.Debug("explicit using v1") } else if strings.Contains(comment, "# syntax=v0") { - g.v = V0 + logrus.Fatal("v0 is no longer supported in envd v1, try to use v1") } else { - logrus.Debug("unknown version, using v0 by default") - g.v = V0 + logrus.Debug("unknown version, using v1 by default") } + g.v = V1 return g, nil } @@ -88,7 +100,8 @@ func (g generalGetter) GetDefaultGraph() ir.Graph { case V1: return v1.DefaultGraph case V0: - return v0.DefaultGraph + logrus.Fatal("v0 is no longer supported in envd v1, try to use v1") + return v1.DefaultGraph default: return nil } @@ -99,7 +112,8 @@ func (g generalGetter) GetDefaultGraphHash() string { case V1: return v1.GetDefaultGraphHash() case V0: - return v0.GetDefaultGraphHash() + logrus.Fatal("v0 is no longer supported in envd v1, try to use v1") + return v1.GetDefaultGraphHash() default: return "" } @@ -110,7 +124,8 @@ func (g generalGetter) GetStarlarkInterpreter(buildContextDir string) starlark.I case V1: return starlarkv1.NewInterpreter(buildContextDir) case V0: - return starlarkv0.NewInterpreter(buildContextDir) + logrus.Fatal("v0 is no longer supported in envd v1, try to use v1") + return starlarkv1.NewInterpreter(buildContextDir) default: return nil } diff --git a/pkg/metrics/collector.go b/pkg/metrics/collector.go index 7391ce9da..5c8a746cb 100644 --- a/pkg/metrics/collector.go +++ b/pkg/metrics/collector.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/metrics/docker_collector.go b/pkg/metrics/docker_collector.go index 4a158bbef..eb3a2e036 100644 --- a/pkg/metrics/docker_collector.go +++ b/pkg/metrics/docker_collector.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -55,7 +55,8 @@ func (c *dockerCollector) Watch(ctx context.Context, cid string) chan Metrics { defer close(stats) err := c.client.Stats(ctx, cid, stats, c.done) if err != nil { - logrus.Error(err) + logrus.WithField("cid", cid). + WithError(err).Error("error occurred in dockerCollector.Watch") } c.running = false }() diff --git a/pkg/metrics/helper.go b/pkg/metrics/helper.go index 80515756c..598f01dfe 100644 --- a/pkg/metrics/helper.go +++ b/pkg/metrics/helper.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/metrics/model.go b/pkg/metrics/model.go index 07b8c7487..c69d75531 100644 --- a/pkg/metrics/model.go +++ b/pkg/metrics/model.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/metrics/widget.go b/pkg/metrics/widget.go index f273f2b9c..234874511 100644 --- a/pkg/metrics/widget.go +++ b/pkg/metrics/widget.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -127,7 +127,6 @@ func colorScale(n int) ui.Color { return ui.ColorGreen } else if n < 80 { return ui.ColorYellow - } else { - return ui.ColorRed } + return ui.ColorRed } diff --git a/pkg/progress/compileui/display.go b/pkg/progress/compileui/display.go index 026bd4f02..1faf1fc28 100644 --- a/pkg/progress/compileui/display.go +++ b/pkg/progress/compileui/display.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ import ( "github.com/sirupsen/logrus" "github.com/tensorchord/envd/pkg/editor/vscode" + progressmode "github.com/tensorchord/envd/pkg/progress/mode" ) type Writer interface { @@ -40,7 +41,7 @@ type generalWriter struct { phase string trace *trace doneCh chan bool - repeatd bool + repeated bool result *Result lineCount int } @@ -48,15 +49,15 @@ type generalWriter struct { func New(ctx context.Context, out console.File, mode string) (Writer, error) { var c console.Console switch mode { - case "auto", "tty", "": + case progressmode.AUTO, progressmode.TTY, progressmode.NONE: if cons, err := console.ConsoleFromFile(out); err == nil { c = cons } else { - if mode == "tty" { + if mode == progressmode.TTY { return nil, errors.Wrap(err, "failed to get console") } } - case "plain": + case progressmode.PLAIN: default: return nil, errors.Errorf("invalid progress mode %s", mode) } @@ -71,7 +72,7 @@ func New(ctx context.Context, out console.File, mode string) (Writer, error) { phase: "parse build.envd and download/cache dependencies", trace: t, doneCh: make(chan bool), - repeatd: false, + repeated: false, result: &Result{ plugins: make([]*PluginInfo, 0), }, @@ -154,10 +155,10 @@ func (w *generalWriter) run(ctx context.Context) error { func (w *generalWriter) output(finished bool) { width, _ := w.getSize() b := aec.EmptyBuilder.Up(uint(1 + w.lineCount)) - if !w.repeatd { + if !w.repeated { b = b.Down(1) } - w.repeatd = true + w.repeated = true if finished { fmt.Fprint(w.console, colorRun) } diff --git a/pkg/progress/compileui/term.go b/pkg/progress/compileui/term.go index 75a7a8dd0..7c56ec11a 100644 --- a/pkg/progress/compileui/term.go +++ b/pkg/progress/compileui/term.go @@ -1,5 +1,5 @@ -// Copyright 2022 The envd Authors -// Copyright 2022 The buildkit Authors +// Copyright 2023 The envd Authors +// Copyright 2023 The buildkit Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/progress/compileui/term_windows.go b/pkg/progress/compileui/term_windows.go index de9da9f9b..ffa7bc281 100644 --- a/pkg/progress/compileui/term_windows.go +++ b/pkg/progress/compileui/term_windows.go @@ -1,5 +1,5 @@ -// Copyright 2022 The envd Authors -// Copyright 2022 The buildkit Authors +// Copyright 2023 The envd Authors +// Copyright 2023 The buildkit Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/progress/compileui/trace.go b/pkg/progress/compileui/trace.go index 6962b8b05..ce450ad39 100644 --- a/pkg/progress/compileui/trace.go +++ b/pkg/progress/compileui/trace.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/progress/compileui/types.go b/pkg/progress/compileui/types.go index 9056bcf47..7d563f42a 100644 --- a/pkg/progress/compileui/types.go +++ b/pkg/progress/compileui/types.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/lang/frontend/starlark/v0/io/const.go b/pkg/progress/mode/mode.go similarity index 83% rename from pkg/lang/frontend/starlark/v0/io/const.go rename to pkg/progress/mode/mode.go index d46769e9e..aa0cbb0f3 100644 --- a/pkg/lang/frontend/starlark/v0/io/const.go +++ b/pkg/progress/mode/mode.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,9 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -package io +package progressmode const ( - ruleCopy = "io.copy" - ruleHTTP = "io.http" + AUTO = "auto" + TTY = "tty" + PLAIN = "plain" + NONE = "" ) diff --git a/pkg/progress/progressui/display.go b/pkg/progress/progressui/display.go index d2e3cdad7..baa1073e8 100644 --- a/pkg/progress/progressui/display.go +++ b/pkg/progress/progressui/display.go @@ -1,5 +1,5 @@ -// Copyright 2022 The envd Authors -// Copyright 2022 The buildkit Authors +// Copyright 2023 The envd Authors +// Copyright 2023 The buildkit Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -133,7 +133,7 @@ type trace struct { w io.Writer startTime *time.Time localTimeDiff time.Duration - vertexes []*vertex + vertices []*vertex byDigest map[digest.Digest]*vertex updates map[digest.Digest]struct{} modeConsole bool @@ -378,7 +378,7 @@ func newTrace(w io.Writer, modeConsole bool) *trace { func (t *trace) warnings() []client.VertexWarning { var out []client.VertexWarning - for _, v := range t.vertexes { + for _, v := range t.vertices { out = append(out, v.warnings...) } return out @@ -478,7 +478,7 @@ func (t *trace) update(s *client.SolveStatus, termWidth int) { if t.localTimeDiff == 0 { t.localTimeDiff = time.Since(*v.Started) } - t.vertexes = append(t.vertexes, t.byDigest[v.Digest]) + t.vertices = append(t.vertices, t.byDigest[v.Digest]) } // allow a duplicate initial vertex that shouldn't reset state if !(prev != nil && prev.isStarted() && v.Started == nil) { @@ -509,7 +509,7 @@ func (t *trace) update(s *client.SolveStatus, termWidth int) { continue } if newlyRevealed { - t.vertexes = append(t.vertexes, group.vertex) + t.vertices = append(t.vertices, group.vertex) } if changed { group.update(1) @@ -584,7 +584,7 @@ func (t *trace) update(s *client.SolveStatus, termWidth int) { } func (t *trace) printErrorLogs(f io.Writer) { - for _, v := range t.vertexes { + for _, v := range t.vertices { if v.Error != "" && !strings.HasSuffix(v.Error, context.Canceled.Error()) { fmt.Fprintln(f, "------") fmt.Fprintf(f, " > %s:\n", v.Name) @@ -625,14 +625,14 @@ func (t *trace) displayInfo() (d displayInfo) { } } - for _, v := range t.vertexes { + for _, v := range t.vertices { if v.jobCached { d.jobs = append(d.jobs, v.jobs...) continue } var jobs []*job j := &job{ - name: strings.Replace(v.Name, "\t", " ", -1), + name: strings.ReplaceAll(v.Name, "\t", " "), vertex: v, isCompleted: true, } diff --git a/pkg/progress/progressui/display_test.go b/pkg/progress/progressui/display_test.go index 2cee2c6fe..3b72bab9c 100644 --- a/pkg/progress/progressui/display_test.go +++ b/pkg/progress/progressui/display_test.go @@ -1,5 +1,5 @@ -// Copyright 2022 The envd Authors -// Copyright 2022 The buildkit Authors +// Copyright 2023 The envd Authors +// Copyright 2023 The buildkit Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/progress/progressui/printer.go b/pkg/progress/progressui/printer.go index f48c4900e..987ab61e2 100644 --- a/pkg/progress/progressui/printer.go +++ b/pkg/progress/progressui/printer.go @@ -1,5 +1,5 @@ -// Copyright 2022 The envd Authors -// Copyright 2022 The buildkit Authors +// Copyright 2023 The envd Authors +// Copyright 2023 The buildkit Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -157,7 +157,7 @@ func (p *textMux) printVtx(t *trace, dgst digest.Digest) { if i == 0 { l = l[v.logsOffset:] } - fmt.Fprintf(p.w, "%s", []byte(l)) + fmt.Fprintf(p.w, "%s", l) if i != len(v.logs)-1 || !v.logsPartial { fmt.Fprintln(p.w, "") } @@ -326,7 +326,7 @@ func (p *textMux) print(t *trace) { } } - // fair split between vertexes + // fair split between vertices if 1.0/(1.0-stats[current].share)*antiFlicker.Seconds() < stats[current].blockTime.Seconds() { p.printVtx(t, max) return diff --git a/pkg/progress/progressui/term.go b/pkg/progress/progressui/term.go index 80a3c87c0..d71562b44 100644 --- a/pkg/progress/progressui/term.go +++ b/pkg/progress/progressui/term.go @@ -1,5 +1,5 @@ -// Copyright 2022 The envd Authors -// Copyright 2022 The buildkit Authors +// Copyright 2023 The envd Authors +// Copyright 2023 The buildkit Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/progress/progressui/term_windows.go b/pkg/progress/progressui/term_windows.go index a18d5a78c..d2a1bfa4f 100644 --- a/pkg/progress/progressui/term_windows.go +++ b/pkg/progress/progressui/term_windows.go @@ -1,5 +1,5 @@ -// Copyright 2022 The envd Authors -// Copyright 2022 The buildkit Authors +// Copyright 2023 The envd Authors +// Copyright 2023 The buildkit Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/progress/progresswriter/printer.go b/pkg/progress/progresswriter/printer.go index 8018867b2..c4165dfaf 100644 --- a/pkg/progress/progresswriter/printer.go +++ b/pkg/progress/progresswriter/printer.go @@ -1,5 +1,5 @@ -// Copyright 2022 The envd Authors -// Copyright 2022 The buildkit Authors +// Copyright 2023 The envd Authors +// Copyright 2023 The buildkit Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import ( "github.com/containerd/console" "github.com/moby/buildkit/client" + progressmode "github.com/tensorchord/envd/pkg/progress/mode" "github.com/tensorchord/envd/pkg/progress/progressui" ) @@ -82,21 +83,21 @@ func NewPrinter(ctx context.Context, out console.File, mode string) (Writer, err done: doneCh, } - if v := os.Getenv("BUILDKIT_PROGRESS"); v != "" && mode == "auto" { + if v := os.Getenv("BUILDKIT_PROGRESS"); v != "" && mode == progressmode.AUTO { mode = v } var c console.Console switch mode { - case "auto", "tty", "": + case progressmode.AUTO, progressmode.TTY, progressmode.NONE: if cons, err := console.ConsoleFromFile(out); err == nil { c = cons } else { - if mode == "tty" { + if mode == progressmode.TTY { return nil, errors.Wrap(err, "failed to get console") } } - case "plain": + case progressmode.PLAIN: default: return nil, errors.Errorf("invalid progress mode %s", mode) } diff --git a/pkg/progress/progresswriter/writer.go b/pkg/progress/progresswriter/writer.go index 5f0628912..b09155079 100644 --- a/pkg/progress/progresswriter/writer.go +++ b/pkg/progress/progresswriter/writer.go @@ -1,5 +1,5 @@ -// Copyright 2022 The envd Authors -// Copyright 2022 The buildkit Authors +// Copyright 2023 The envd Authors +// Copyright 2023 The buildkit Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/remote/sshd/os.go b/pkg/remote/sshd/os.go index 898148ec2..6ae3d9fa7 100644 --- a/pkg/remote/sshd/os.go +++ b/pkg/remote/sshd/os.go @@ -1,5 +1,5 @@ -// Copyright 2022 The envd Authors -// Copyright 2022 The okteto remote Authors +// Copyright 2023 The envd Authors +// Copyright 2023 The okteto remote Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/remote/sshd/sshd.go b/pkg/remote/sshd/sshd.go index 113cd0677..aaf380bab 100644 --- a/pkg/remote/sshd/sshd.go +++ b/pkg/remote/sshd/sshd.go @@ -1,5 +1,5 @@ -// Copyright 2022 The envd Authors -// Copyright 2022 The okteto remote Authors +// Copyright 2023 The envd Authors +// Copyright 2023 The okteto remote Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -181,7 +181,7 @@ func (srv *Server) connectionHandler(s ssh.Session) { err := s.Exit(0) if err != nil { - logger.Warningln("exit session with error:", err) + logger.WithError(err).Warnln("exit session with error") } return } @@ -194,7 +194,7 @@ func (srv *Server) connectionHandler(s ssh.Session) { err := s.Exit(0) if err != nil { - logger.Warningln("exit session with error:", err) + logger.WithError(err).Warnln("exit session with error") } } @@ -256,13 +256,13 @@ func setWinSize(f *os.File, w, h int) { } } -func sendErrAndExit(logger *logrus.Entry, s ssh.Session, err error) { - msg := strings.TrimPrefix(err.Error(), "exec: ") +func sendErrAndExit(logger *logrus.Entry, s ssh.Session, exitErr error) { + msg := strings.TrimPrefix(exitErr.Error(), "exec: ") if _, err := s.Stderr().Write([]byte(msg)); err != nil { logger.WithError(err).Errorf("failed to write error back to session") } - if err := s.Exit(getExitStatusFromError(err)); err != nil { + if err := s.Exit(getExitStatusFromError(exitErr)); err != nil { logger.WithError(err).Errorf("pty session failed to exit") } } @@ -272,7 +272,7 @@ func getExitStatusFromError(err error) int { return 0 } - var exitErr exec.ExitError + var exitErr *exec.ExitError if ok := errors.As(err, &exitErr); !ok { return 1 } diff --git a/pkg/shell/zsh.go b/pkg/shell/zsh.go index 87a3a10e9..b62b0afbf 100644 --- a/pkg/shell/zsh.go +++ b/pkg/shell/zsh.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -101,7 +101,7 @@ func (m generalManager) DownloadOrCache() (bool, error) { Name: "origin", URLs: []string{url}, Fetch: []config.RefSpec{ - config.RefSpec("+refs/heads/master:refs/remotes/origin/master"), + "+refs/heads/master:refs/remotes/origin/master", }, } cfg.Branches["master"] = &config.Branch{ diff --git a/pkg/shell/zsh_suite_test.go b/pkg/shell/zsh_suite_test.go index 05d074126..e70d313da 100644 --- a/pkg/shell/zsh_suite_test.go +++ b/pkg/shell/zsh_suite_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/shell/zsh_test.go b/pkg/shell/zsh_test.go index 7f1b297d1..da1ba5ec3 100644 --- a/pkg/shell/zsh_test.go +++ b/pkg/shell/zsh_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/ssh/config/entry.go b/pkg/ssh/config/entry.go index 2aaa2d4c3..45a619ea5 100644 --- a/pkg/ssh/config/entry.go +++ b/pkg/ssh/config/entry.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ type EntryOptions struct { // AddEntry adds an entry to the user's sshconfig func AddEntry(eo EntryOptions) error { - eo.Name = buildHostname(eo.Name) + eo.Name = BuildHostname(eo.Name) err := add(getSSHConfigPath(), eo) if err != nil { return err @@ -62,7 +62,6 @@ func add(path string, eo EntryOptions) error { host := newHost([]string{eo.Name}, []string{"entry generated by envd"}) host.params = []*param{ - newParam(forwardAgentKeyword, []string{"yes"}, nil), newParam(pubkeyAcceptedKeyTypesKeyword, []string{"+ssh-rsa"}, nil), newParam(hostKeyAlgorithms, []string{"+ssh-rsa"}, nil), newParam(hostNameKeyword, []string{eo.IFace}, nil), @@ -89,7 +88,7 @@ func add(path string, eo EntryOptions) error { // RemoveEntry removes the entry to the user's sshconfig if found func RemoveEntry(name string) error { - err := remove(getSSHConfigPath(), buildHostname(name)) + err := remove(getSSHConfigPath(), BuildHostname(name)) if err != nil { return err } @@ -99,7 +98,7 @@ func RemoveEntry(name string) error { if err != nil { return err } - err = remove(winSshConfig, buildHostname(name)) + err = remove(winSshConfig, BuildHostname(name)) if err != nil { return err } diff --git a/pkg/ssh/config/key.go b/pkg/ssh/config/key.go index 8151096c8..63b29e24c 100644 --- a/pkg/ssh/config/key.go +++ b/pkg/ssh/config/key.go @@ -1,10 +1,11 @@ -// Copyright 2022 The envd Authors -// Copyright 2022 The Okteto Authors +// Copyright 2023 The envd Authors +// Copyright 2023 The Okteto 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 +// 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, diff --git a/pkg/ssh/config/ssh_config.go b/pkg/ssh/config/ssh_config.go index 418757255..f296e75e8 100644 --- a/pkg/ssh/config/ssh_config.go +++ b/pkg/ssh/config/ssh_config.go @@ -1,5 +1,5 @@ -// Copyright 2022 The envd Authors -// Copyright 2022 The Okteto Authors +// Copyright 2023 The envd Authors +// Copyright 2023 The Okteto Authors // based on https://github.com/havoc-io/sshconfig // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -232,7 +232,8 @@ func (config *sshConfig) writeTo(w io.Writer) error { func (config *sshConfig) writeToFilepath(p string) error { sshDir := filepath.Dir(p) if err := os.MkdirAll(sshDir, 0700); err != nil { - logrus.Infof("failed to create SSH directory %s: %s", sshDir, err) + logrus.WithError(err). + Infof("failed to create SSH directory %s", sshDir) } stat, err := os.Stat(p) @@ -301,7 +302,7 @@ func (h *host) getParam(keyword string) *param { return nil } -func buildHostname(name string) string { +func BuildHostname(name string) string { return fmt.Sprintf("%s.envd", name) } @@ -386,7 +387,7 @@ func GetPort(name string) (int, error) { return 0, err } - hostname := buildHostname(name) + hostname := BuildHostname(name) i, found := findHost(cfg, hostname) if !found { return 0, errors.Newf("development container not found") @@ -477,7 +478,7 @@ func save(cfg *sshConfig, path string) error { func getSSHConfigPath() string { dirname, err := os.UserHomeDir() if err != nil { - logrus.Fatal(err) + logrus.WithError(err).Fatal() } return filepath.Join(dirname, ".ssh", "config") } diff --git a/pkg/ssh/config/ssh_config_suite_test.go b/pkg/ssh/config/ssh_config_suite_test.go index 6095729c6..7abc3d8cd 100644 --- a/pkg/ssh/config/ssh_config_suite_test.go +++ b/pkg/ssh/config/ssh_config_suite_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/ssh/config/ssh_config_test.go b/pkg/ssh/config/ssh_config_test.go index 5e731e1b2..9a7051f6e 100644 --- a/pkg/ssh/config/ssh_config_test.go +++ b/pkg/ssh/config/ssh_config_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -27,7 +27,7 @@ var _ = Describe("ssh config", func() { port := 8888 keyPath := "key" eo := EntryOptions{ - Name: buildHostname(env), + Name: BuildHostname(env), IFace: iface, Port: port, PrivateKeyPath: keyPath, diff --git a/pkg/ssh/copy.go b/pkg/ssh/copy.go index b5a3b43b8..afd0df2f5 100644 --- a/pkg/ssh/copy.go +++ b/pkg/ssh/copy.go @@ -1,10 +1,11 @@ -// Copyright 2022 The envd Authors -// Copyright 2022 The Okteto Authors +// Copyright 2023 The envd Authors +// Copyright 2023 The Okteto 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 +// 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, @@ -48,13 +49,13 @@ func (c *Copier) Copy() { nw, ew := c.Remote.Write(buf[write:nr]) write += nw if ew != nil { - logrus.Debugf("write to remote terminal error: %s", ew.Error()) + logrus.WithError(ew).Debug("write to remote terminal error") <-t.C continue } } if er != nil { - logrus.Debugf("read from local to remote terminal error: %s", er.Error()) + logrus.WithError(er).Debug("read from local to remote terminal error") return } } diff --git a/pkg/ssh/ssh.go b/pkg/ssh/ssh.go index 8309d3e24..f1732fc10 100644 --- a/pkg/ssh/ssh.go +++ b/pkg/ssh/ssh.go @@ -1,6 +1,6 @@ -// Copyright 2022 The envd Authors -// Copyright 2022 The okteto Authors -// Copyright 2022 stefanprodan +// Copyright 2023 The envd Authors +// Copyright 2023 The okteto Authors +// Copyright 2023 stefanprodan // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -40,6 +40,7 @@ type Client interface { Attach() error ExecWithOutput(cmd string) ([]byte, error) LocalForward(localAddress, targetAddress string) error + RemoteForward(localAddress, targetAddress string) error Close() error } @@ -84,13 +85,14 @@ type generalClient struct { } func NewClient(opt Options) (Client, error) { - logrus.WithFields(logrus.Fields{ + logger := logrus.WithFields(logrus.Fields{ "user": opt.User, "port": opt.Port, "server": opt.Server, "agent-forwarding": opt.AgentForwarding, "auth": opt.Auth, - }).Debug("ssh to the environment") + }) + logger.Debug("ssh to the environment") config := &ssh.ClientConfig{ User: opt.User, @@ -145,7 +147,7 @@ func NewClient(opt Options) (Client, error) { return nil, errors.Wrap(err, "forwarding agent to client failed") } } else { - logrus.Warn("SSH Agent Forwarding is disabled. This will have no impact on your normal use if you do not use the ssh key on the host.") + logger.Warn("SSH Agent Forwarding is disabled. This will have no impact on your normal use if you do not use the ssh key on the host.") } } @@ -192,6 +194,14 @@ func (c generalClient) Attach() error { } } + logger := logrus.WithFields(logrus.Fields{ + "user": c.opt.User, + "port": c.opt.Port, + "server": c.opt.Server, + "agent-forwarding": c.opt.AgentForwarding, + "auth": c.opt.Auth, + }) + modes := ssh.TerminalModes{ ssh.ECHO: 0, // Disable echoing ssh.ECHOCTL: 0, // Don't print control chars @@ -205,15 +215,15 @@ func (c generalClient) Attach() error { var ok bool if termFD, ok = isTerminal(os.Stdin); ok { width, height, err = term.GetSize(int(os.Stdout.Fd())) - logrus.Debugf("terminal width %d height %d", width, height) + logger.Debugf("terminal width %d height %d", width, height) if err != nil { - logrus.Debugf("request for terminal size failed: %s", err) + logger.WithError(err).Debug("request for terminal size failed") } } state, err := term.MakeRaw(termFD) if err != nil { - logrus.Debugf("request for raw terminal failed: %s", err) + logger.WithError(err).Debug("request for raw terminal failed") } defer func() { @@ -222,10 +232,10 @@ func (c generalClient) Attach() error { } if err := term.Restore(termFD, state); err != nil { - logrus.Debugf("failed to restore terminal: %s", err) + logger.WithError(err).Debugf("failed to restore terminal") } - logrus.Debugf("terminal restored") + logger.Debugf("terminal restored") }() if err := session.RequestPty("xterm-256color", height, width, modes); err != nil { @@ -236,12 +246,12 @@ func (c generalClient) Attach() error { session.Stderr = os.Stderr session.Stdin = os.Stdin - logrus.Debug("starting shell") + logger.Debug("starting shell") err = session.Shell() if err != nil { return errors.Wrap(err, "starting shell failed") } - logrus.Debug("waiting for shell to exit") + logger.Debug("waiting for shell to exit") if err = session.Wait(); err != nil { var ee *ssh.ExitError if ok := errors.As(err, &ee); ok { @@ -249,18 +259,18 @@ func (c generalClient) Attach() error { case 130: return nil case 137: - logrus.Warn(`Insufficient memory.`) + logger.Warn(`Insufficient memory.`) } } var emr *ssh.ExitMissingError if ok := errors.As(err, &emr); ok { - logrus.Debugf("exit status missing: %s", emr) + logger.WithError(emr).Debug("exit status missing") return nil } return errors.Wrap(err, "waiting for session failed") } - logrus.Debug("shell exited") + logger.Debug("shell exited") return nil } @@ -270,7 +280,9 @@ func (c generalClient) LocalForward(localAddress, targetAddress string) error { return errors.Wrap(err, "net.Listen failed") } - logrus.Debug("begin to forward " + localAddress + " to " + targetAddress) + logger := logrus.WithField("type", "local") + + logger.Debugf("begin to forward %s to %s", localAddress, targetAddress) for { localCon, err := localListener.Accept() if err != nil { @@ -286,7 +298,7 @@ func (c generalClient) LocalForward(localAddress, targetAddress string) error { go func() { _, err = io.Copy(sshConn, localCon) if err != nil { - logrus.Debugf("io.Copy failed: %v", err) + logger.WithError(err).Debug("io.Copy failed") } }() @@ -294,7 +306,45 @@ func (c generalClient) LocalForward(localAddress, targetAddress string) error { go func() { _, err = io.Copy(localCon, sshConn) if err != nil { - logrus.Debugf("io.Copy failed: %v", err) + logger.WithError(err).Debug("io.Copy failed") + } + }() + } +} + +func (c generalClient) RemoteForward(remoteAddress, targetAddress string) error { + sshListener, err := c.cli.Listen("tcp", remoteAddress) + if err != nil { + return errors.Wrap(err, "cli.Listen failed") + } + + logger := logrus.WithField("type", "remote") + + logger.Debugf("begin to forward %s to %s", remoteAddress, targetAddress) + for { + sshCon, err := sshListener.Accept() + if err != nil { + return errors.Wrap(err, "listen.Accept failed") + } + + targetCon, err := net.Dial("tcp", targetAddress) + if err != nil { + return errors.Wrap(err, "net.Dial failed") + } + + // Copy sshCon.Reader to targetCon.Writer + go func() { + _, err = io.Copy(targetCon, sshCon) + if err != nil { + logger.WithError(err).Debug("io.Copy failed") + } + }() + + // Copy targetCon.Reader to sshCon.Writer + go func() { + _, err = io.Copy(sshCon, targetCon) + if err != nil { + logger.WithError(err).Debug("io.Copy failed") } }() } @@ -357,23 +407,20 @@ func parsePemBlock(block *pem.Block) (interface{}, error) { key, err := x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { return nil, errors.Newf("Parsing PKCS private key failed %w", err) - } else { - return key, nil } + return key, nil case "EC PRIVATE KEY": key, err := x509.ParseECPrivateKey(block.Bytes) if err != nil { return nil, errors.Newf("Parsing EC private key failed %w", err) - } else { - return key, nil } + return key, nil case "DSA PRIVATE KEY": key, err := ssh.ParseDSAPrivateKey(block.Bytes) if err != nil { return nil, errors.Newf("Parsing DSA private key failed %w", err) - } else { - return key, nil } + return key, nil default: return nil, errors.Newf("Parsing private key failed, unsupported key type %q", block.Type) } diff --git a/pkg/syncthing/client.go b/pkg/syncthing/client.go new file mode 100644 index 000000000..982b59c46 --- /dev/null +++ b/pkg/syncthing/client.go @@ -0,0 +1,96 @@ +// Copyright 2023 The envd 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 syncthing + +import ( + "bytes" + "fmt" + "io" + "net/http" + "time" + + "github.com/sirupsen/logrus" +) + +func NewAPIClient() *http.Client { + return &http.Client{ + Timeout: 60 * time.Second, + } +} + +const ( + GET = "GET" + POST = "POST" + PUT = "PUT" +) + +type Client struct { + ApiKey string + Client *http.Client + BasePath string +} + +func (s *Syncthing) NewClient() *Client { + return &Client{ + ApiKey: s.ApiKey, + Client: NewAPIClient(), + BasePath: fmt.Sprintf("http://127.0.0.1:%s", s.Port), + } +} + +// Makes API calls to the syncthing instance's rest api +func (c *Client) SendRequest(method string, url string, params map[string]string, body []byte) ([]byte, error) { + logrus.WithFields(logrus.Fields{ + "method": method, + "url": url, + "params": params, + }).Debug("calling syncthing API") + // TODO: can implement retry logic + + var urlPath = c.BasePath + url + + req, err := http.NewRequest(method, urlPath, bytes.NewBuffer(body)) + if err != nil { + return nil, fmt.Errorf("failed to initialize syncthing API request: %w", err) + } + + req.Header.Set("X-API-Key", c.ApiKey) + + q := req.URL.Query() + + for key, value := range params { + q.Add(key, value) + } + + req.URL.RawQuery = q.Encode() + + resp, err := c.Client.Do(req) + if err != nil { + return nil, fmt.Errorf("failed to call syncthing [%s]: %w", url, err) + } + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("failed to call syncthing [%s]: %s", url, resp.Status) + } + + defer resp.Body.Close() + + body, err = io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("failed to read response from syncthing [%s]: %w", url, err) + } + + return body, nil +} diff --git a/pkg/syncthing/config.go b/pkg/syncthing/config.go new file mode 100644 index 000000000..adb891f73 --- /dev/null +++ b/pkg/syncthing/config.go @@ -0,0 +1,133 @@ +// Copyright 2023 The envd 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 syncthing + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/sirupsen/logrus" + "github.com/syncthing/syncthing/lib/config" +) + +const ( + DefaultLocalPort = "8386" + DefaultRemotePort = "8384" + DefaultRemoteAPIAddress = "127.0.0.1:8384" + DefaultApiKey = "envd" + DefaultLocalDeviceAddress = "tcp://127.0.0.1:22000" + DefaultRemoteDeviceAddress = "tcp://127.0.0.1:22001" +) + +// @source: https://docs.syncthing.net/users/config.html +func InitLocalConfig() *config.Configuration { + return &config.Configuration{ + Version: 37, + GUI: config.GUIConfiguration{ + Enabled: true, + RawAddress: fmt.Sprintf("127.0.0.1:%s", DefaultLocalPort), + APIKey: DefaultApiKey, + Theme: "default", + }, + Options: config.OptionsConfiguration{ + GlobalAnnEnabled: false, + LocalAnnEnabled: false, + ReconnectIntervalS: 1, + StartBrowser: false, + NATEnabled: false, + URAccepted: -1, + URPostInsecurely: false, + URInitialDelayS: 1800, + AutoUpgradeIntervalH: 0, // Disable auto upgrade + StunKeepaliveStartS: 0, // Disable STUN keepalive\ + }, + } +} + +// Fetches the latest configuration from the syncthing rest api +func (s *Syncthing) GetConfig() (*config.Configuration, error) { + resBody, err := s.Client.SendRequest(GET, "/rest/config", nil, nil) + if err != nil { + return nil, fmt.Errorf("failed to fetch syncthing config: %w", err) + } + + cfg := &config.Configuration{} + err = json.Unmarshal(resBody, cfg) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal syncthing config: %w", err) + } + + return cfg, nil +} + +// Fetches the latest configuration from the syncthing rest api and applies it to the syncthing struct +func (s *Syncthing) PullLatestConfig() error { + logrus.Debugf("Pulling latest config for: %s", s.Name) + cfg, err := s.GetConfig() + if err != nil { + return fmt.Errorf("failed to fetch syncthing config: %w", err) + } + + s.Config = cfg + s.PrevConfig = cfg.Copy() + return nil +} + +func (s *Syncthing) WaitForConfigApply(timeout time.Duration) error { + start := time.Now() + for { + if time.Since(start) > timeout { + logrus.WithField("timeout", timeout). + Debug("Timeout reached, config not applied") + return fmt.Errorf("timed out waiting for configurations to apply") + } + + events, err := s.GetConfigSavedEvents() + if err != nil { + return fmt.Errorf("failed to get syncthing config saved events: %w", err) + } + + if len(events) > 0 { + err := s.PullLatestConfig() + if err != nil { + return fmt.Errorf("failed to pull latest config: %w", err) + } + return nil + } + + time.Sleep(500 * time.Millisecond) + } +} + +// Applies the config to the syncthing instance and waits for the config to be applied +func (s *Syncthing) ApplyConfig() error { + configByte, err := GetConfigBytes(s.Config, JSON) + if err != nil { + return fmt.Errorf("failed to marshal syncthing config: %w", err) + } + + _, err = s.Client.SendRequest(PUT, "/rest/config", nil, configByte) + if err != nil { + return fmt.Errorf("failed to apply syncthing config: %w", err) + } + + err = s.WaitForConfigApply(10 * time.Second) + if err != nil { + return fmt.Errorf("failed to wait for syncthing config apply: %w", err) + } + + return nil +} diff --git a/pkg/syncthing/device.go b/pkg/syncthing/device.go new file mode 100644 index 000000000..fbc407864 --- /dev/null +++ b/pkg/syncthing/device.go @@ -0,0 +1,68 @@ +// Copyright 2023 The envd 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 syncthing + +import ( + "fmt" + "strings" + + "github.com/sirupsen/logrus" +) + +func ConnectDevices(s1 *Syncthing, s2 *Syncthing) error { + logger := logrus.WithFields(logrus.Fields{ + "s1": s1.Name, + "s2": s2.Name, + "config-s1": s1.Config, + "config-s2": s2.Config, + }) + logger.Debug("Connecting syncthings") + var err error + connectedDevices := append(s1.Config.Devices, s2.Config.Devices...) + + s1.Config.Devices = connectedDevices + s2.Config.Devices = connectedDevices + + logger.Debug("Adding device config for: ", s1.Name) + err = s1.ApplyConfig() + if err != nil { + return err + } + + logger.Debug("Adding device config for: ", s2.Name) + err = s2.ApplyConfig() + if err != nil { + return err + } + + return nil +} + +// This method can only be called when the devices are not connected +func (s *Syncthing) SetDeviceAddress(addr string) (err error) { + if len(s.Config.Devices) == 0 { + return fmt.Errorf("no devices found") + } + s.DeviceAddress = addr + + // Could work on better identifying which is the current device + s.Config.Devices[0].Addresses = []string{addr} + return nil +} + +func (s *Syncthing) GetDeviceAddressPort() string { + splitLst := strings.Split(s.DeviceAddress, ":") + return splitLst[len(splitLst)-1] +} diff --git a/pkg/syncthing/event.go b/pkg/syncthing/event.go new file mode 100644 index 000000000..2a68e5a1a --- /dev/null +++ b/pkg/syncthing/event.go @@ -0,0 +1,101 @@ +// Copyright 2023 The envd 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 syncthing + +import ( + "encoding/json" + "fmt" + "strconv" + + "github.com/sirupsen/logrus" + "github.com/syncthing/syncthing/lib/config" +) + +// Status represents the status of a syncthing folder. +type Status struct { + State string `json:"state"` + PullErrors int64 `json:"pullErrors"` +} + +const ConfigSaved = "ConfigSaved" + +type ConfigSavedEvent struct { + Type string `json:"type"` + Id int64 `json:"id"` + GlobalId int64 `json:"globalID"` + Time string `json:"time"` + Data config.Configuration `json:"data"` +} + +type GeneralEvent struct { + Type string `json:"type"` + Id int64 `json:"id"` + GlobalId int64 `json:"globalID"` + Time string `json:"time"` + Data interface{} `json:"data"` +} + +// Fetches the most recent event using the syncthing rest api +func (s *Syncthing) GetMostRecentEvent() (*GeneralEvent, error) { + resBody, err := s.Client.SendRequest(GET, "/rest/events", nil, nil) + if err != nil { + return nil, fmt.Errorf("failed to get most recent event: %w", err) + } + + var events []*GeneralEvent + err = json.Unmarshal(resBody, &events) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal most recent event: %w", err) + } + + latestEvent := events[len(events)-1] + s.latestEventId = latestEvent.Id + + // Assuming that the events are returned in order + return events[len(events)-1], nil +} + +// Fetches the latest config saved events using the syncthing rest api starting from the latest event id +func (s *Syncthing) GetConfigSavedEvents() ([]*ConfigSavedEvent, error) { + logrus.Debugf("Getting config saved events") + params := map[string]string{ + "since": strconv.FormatInt(s.latestEventId, 10), + "timeout": "0", + } + resBody, err := s.Client.SendRequest(GET, "/rest/events", params, nil) + if err != nil { + return nil, fmt.Errorf("failed to get config saved event: %w", err) + } + + var allEvents []*ConfigSavedEvent + err = json.Unmarshal(resBody, &allEvents) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal config saved event: %w", err) + } + + var events []*ConfigSavedEvent + for _, event := range allEvents { + if event.Type == ConfigSaved { + events = append(events, event) + } + } + + if len(allEvents) > 0 { + latestEvent := allEvents[len(allEvents)-1] + s.latestEventId = latestEvent.Id + } + + return events, nil +} diff --git a/pkg/syncthing/file.go b/pkg/syncthing/file.go new file mode 100644 index 000000000..404012408 --- /dev/null +++ b/pkg/syncthing/file.go @@ -0,0 +1,108 @@ +// Copyright 2023 The envd 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 syncthing + +import ( + "encoding/json" + "encoding/xml" + "fmt" + "os" + "path/filepath" + + "github.com/syncthing/syncthing/lib/config" + + "github.com/tensorchord/envd/pkg/util/fileutil" +) + +func GetHomeDirectory() (string, error) { + return fileutil.ConfigFile("syncthing") +} + +func GetConfigFilePath(homeDirectory string) string { + return filepath.Join(homeDirectory, "config.xml") +} + +func (s *Syncthing) WriteLocalConfig() error { + configBytes, err := GetConfigBytes(s.Config, XML) + if err != nil { + return fmt.Errorf("failed to get syncthing config bytes: %w", err) + } + + err = os.MkdirAll(s.HomeDirectory, 0777) + if err != nil { + return fmt.Errorf("failed to : %w", err) + } + + configFilePath := GetConfigFilePath(s.HomeDirectory) + if err = fileutil.CreateIfNotExist(configFilePath); err != nil { + return fmt.Errorf("failed to get syncthing config file path: %w", err) + } + + if err = os.WriteFile(configFilePath, configBytes, 0666); err != nil { + return fmt.Errorf("failed to write syncthing config file: %w", err) + } + return nil +} + +func CleanLocalConfig(name string) error { + configPath, err := GetHomeDirectory() + if err != nil { + return fmt.Errorf("failed to get syncthing config file path: %w", err) + } + + if err := os.RemoveAll(configPath); err != nil { + return fmt.Errorf("failed to remove syncthing config file: %w", err) + } + return nil +} + +const ( + XML = "xml" + JSON = "json" +) + +// Get syncthing configuration in bytes with format XML or JSON +func GetConfigBytes(cfg *config.Configuration, outputType string) (configByte []byte, err error) { + xmlStruct := struct { + XMLName xml.Name `xml:"configuration"` + *config.Configuration + }{ + Configuration: cfg, + } + + jsonStruct := struct { + *config.Configuration + }{ + Configuration: cfg, + } + + switch outputType { + case XML: + + configByte, err = xml.MarshalIndent(xmlStruct, "", " ") + if err != nil { + return []byte{}, err + } + case JSON: + configByte, err = json.MarshalIndent(jsonStruct, "", " ") + if err != nil { + return []byte{}, err + } + default: + return []byte{}, fmt.Errorf("invalid output type: %s", outputType) + } + + return configByte, nil +} diff --git a/pkg/syncthing/folder.go b/pkg/syncthing/folder.go new file mode 100644 index 000000000..94f84c70d --- /dev/null +++ b/pkg/syncthing/folder.go @@ -0,0 +1,55 @@ +// Copyright 2023 The envd 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 syncthing + +import "github.com/syncthing/syncthing/lib/config" + +func SyncFolder(s1 *Syncthing, s2 *Syncthing, dir1 string, dir2 string) error { + baseFolder := config.FolderConfiguration{ + ID: "default", + RescanIntervalS: 5, + FSWatcherEnabled: true, + FSWatcherDelayS: 10, + Devices: []config.FolderDeviceConfiguration{ + { + DeviceID: s1.DeviceID, + }, + { + DeviceID: s2.DeviceID, + }, + }, + } + + s1Folder := baseFolder.Copy() + s2Folder := baseFolder.Copy() + + s1Folder.Path = dir1 + s2Folder.Path = dir2 + + s1.Config.SetFolder(s1Folder) + s2.Config.SetFolder(s2Folder) + + err := s1.ApplyConfig() + if err != nil { + return err + } + + err = s2.ApplyConfig() + if err != nil { + return err + } + + return nil +} diff --git a/pkg/syncthing/install.go b/pkg/syncthing/install.go new file mode 100644 index 000000000..3ecf3e5a6 --- /dev/null +++ b/pkg/syncthing/install.go @@ -0,0 +1,143 @@ +// Copyright 2023 The envd 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 syncthing + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + + "github.com/cockroachdb/errors" + "github.com/hashicorp/go-getter" + "github.com/sirupsen/logrus" + + "github.com/tensorchord/envd/pkg/util/fileutil" +) + +func getSyncthingVersion() string { + // TODO: Better versioning + return "1.22.2" +} + +func getSyncthingInstallPath() string { + return filepath.Join(fileutil.DefaultCacheDir, "bin") +} + +func GetSyncthingBinPath() string { + return filepath.Join(getSyncthingInstallPath(), "syncthing") +} + +func getSyncthingDownloadURL(os, arch, version string) (string, error) { + // TODO: double check os/arch support + fileExtension := "tar.gz" + if os == "windows" || os == "macos" { + fileExtension = "zip" + } + + downloadUrl := "https://github.com/syncthing/syncthing/releases/download/v%[3]s/syncthing-%[1]s-%[2]s-v%[3]s.%[4]s" + switch os { + case "linux": + switch arch { + case "amd64", "arm64": + return fmt.Sprintf(downloadUrl, os, arch, version, fileExtension), nil + } + case "macos": + switch arch { + case "amd64", "arm64": + return fmt.Sprintf(downloadUrl, os, arch, version, fileExtension), nil + } + case "windows": + switch arch { + case "amd64": + return fmt.Sprintf(downloadUrl, os, arch, version, fileExtension), nil + } + } + + return "", errors.New(fmt.Sprintf("%s-%s is not a supported platform for syncthing", os, arch)) +} + +func getSyncthingDownloadFolderName(os, arch, version string) string { + return fmt.Sprintf("syncthing-%[1]s-%[2]s-v%[3]s", os, arch, version) +} + +func IsInstalled() bool { + if _, err := os.Stat(GetSyncthingBinPath()); err != nil { + return false + } + return true +} + +func (s *Syncthing) CleanupSyncthing() error { + logrus.Debug("Cleaning up syncthing") + + err := os.RemoveAll(s.HomeDirectory) + if err != nil { + return errors.Wrap(err, "failed to remove syncthing config file: ") + } + + return nil +} + +func InstallSyncthing() error { + logrus.Debug("Installing syncthing") + if IsInstalled() { + logrus.Debug("Syncthing is already installed, skipping installation") + return nil + } + + var operatingSystem = runtime.GOOS + var arch = runtime.GOARCH + var version = getSyncthingVersion() + if operatingSystem == "darwin" { + operatingSystem = "macos" + } + + downloadUrl, err := getSyncthingDownloadURL(operatingSystem, arch, version) + if err != nil { + return err + } + + logrus.Debug("Downloading syncthing from ", downloadUrl) + client := &getter.Client{ + Src: downloadUrl, + Dst: getSyncthingInstallPath(), + Mode: getter.ClientModeDir, + } + + if err := client.Get(); err != nil { + return err + } + + var downloadFolder = fmt.Sprintf("%s/%s", getSyncthingInstallPath(), getSyncthingDownloadFolderName(operatingSystem, arch, version)) + + err = os.Rename(fmt.Sprintf("%s/syncthing", downloadFolder), GetSyncthingBinPath()) + if err != nil { + return err + } + + err = os.RemoveAll(downloadFolder) + if err != nil { + return err + } + + if err := os.Chmod(GetSyncthingBinPath(), 0755); err != nil { + return err + } + + logrus.Info("Syncthing installed successfully!") + + return nil +} diff --git a/pkg/syncthing/syncthing.go b/pkg/syncthing/syncthing.go new file mode 100644 index 000000000..3d126088f --- /dev/null +++ b/pkg/syncthing/syncthing.go @@ -0,0 +1,270 @@ +// Copyright 2023 The envd 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 syncthing + +import ( + "fmt" + "os" + "os/exec" + "os/signal" + "syscall" + "time" + + "github.com/sirupsen/logrus" + "github.com/syncthing/syncthing/lib/config" + "github.com/syncthing/syncthing/lib/protocol" +) + +type Syncthing struct { + Name string + Cmd *exec.Cmd + Config *config.Configuration + PrevConfig config.Configuration // Unapplied config + HomeDirectory string + Port string + DeviceID protocol.DeviceID + Client *Client + ApiKey string + latestEventId int64 + DeviceAddress string +} + +// Initializes the remote syncthing instance +func InitializeRemoteSyncthing() (*Syncthing, error) { + s := &Syncthing{ + Name: "Remote Syncthing", + Port: DefaultRemotePort, + HomeDirectory: "/config", + ApiKey: DefaultApiKey, + } + + logger := logrus.WithFields(logrus.Fields{ + "name": s.Name, + "port": s.Port, + "homedir": s.HomeDirectory, + "apikey": s.ApiKey, + "config": s.Config, + }) + + s.Client = s.NewClient() + + err := s.WaitForStartup(15 * time.Second) + if err != nil { + return nil, fmt.Errorf("failed to wait for syncthing startup: %w", err) + } + + err = s.PullLatestConfig() + if err != nil { + return nil, fmt.Errorf("failed to pull latest config: %w", err) + } + + logger.Debug("Remote syncthing connected") + + err = s.SetDeviceAddress(DefaultRemoteDeviceAddress) + if err != nil { + return nil, err + } + s.Config.Options.RawListenAddresses = []string{DefaultRemoteDeviceAddress} + s.DeviceID = s.Config.Devices[0].DeviceID + + err = s.ApplyConfig() + if err != nil { + return nil, err + } + + return s, nil +} + +// Initializes the local syncthing instance +func InitializeLocalSyncthing(name string) (*Syncthing, error) { + + initConfig := InitLocalConfig() + homeDirectory, err := GetHomeDirectory() + if err != nil { + return nil, err + } + + s := &Syncthing{ + Name: "Local Syncthing", + Config: initConfig, + HomeDirectory: homeDirectory, + ApiKey: DefaultApiKey, + Port: ParsePortFromAddress(initConfig.GUI.Address()), + } + + logger := logrus.WithFields(logrus.Fields{ + "name": s.Name, + "port": s.Port, + "homedir": s.HomeDirectory, + "apikey": s.ApiKey, + "config": s.Config, + }) + + s.Client = s.NewClient() + + logger.Debugf("Port for local syncthing is: %s", initConfig.GUI.Address()) + + if err != nil { + return nil, err + } + + if err = s.WriteLocalConfig(); err != nil { + return nil, err + } + + err = s.StartLocalSyncthing() + if err != nil { + return nil, err + } + + err = s.PullLatestConfig() + if err != nil { + return nil, fmt.Errorf("failed to pull latest config: %w", err) + } + + err = s.SetDeviceAddress(DefaultLocalDeviceAddress) + if err != nil { + return nil, err + } + + s.Config.Options.RawListenAddresses = []string{DefaultLocalDeviceAddress} + s.DeviceID = s.Config.Devices[0].DeviceID + + err = s.ApplyConfig() + if err != nil { + return nil, err + } + + return s, nil +} + +func (s *Syncthing) StartLocalSyncthing() error { + if !IsInstalled() { + err := InstallSyncthing() + if err != nil { + return fmt.Errorf("failed to install syncthing: %w", err) + } + } + + logger := logrus.WithFields(logrus.Fields{ + "name": s.Name, + "port": s.Port, + "homedir": s.HomeDirectory, + "apikey": s.ApiKey, + "config": s.Config, + }) + + logger.Debug("Starting local syncthing...") + cmd := exec.Command(GetSyncthingBinPath(), "-no-restart", "-no-browser", "-home", s.HomeDirectory) + + err := cmd.Start() + if err != nil { + return fmt.Errorf("failed to run syncthing executable: %w", err) + } + s.Cmd = cmd + logger.Debug("Local syncthing started!") + + err = s.WaitForStartup(10 * time.Second) + if err != nil { + return fmt.Errorf("failed to wait for syncthing startup: %w", err) + } + + // Handle the SIGINT signal + signalChan := make(chan os.Signal, 1) + signal.Notify(signalChan, syscall.SIGINT) + + go func() { + <-signalChan + + err := cmd.Process.Signal(os.Interrupt) + if err != nil { + logger.WithError(err).Error("Failed to send SIGINT to syncthing") + } + + err = cmd.Wait() + if err != nil { + logger.WithError(err).Error("Failed to wait for syncthing to exit") + } + + os.Exit(0) + }() + + return nil +} + +func (s *Syncthing) Ping() (bool, error) { + _, err := s.Client.SendRequest(GET, "/rest/system/ping", nil, nil) + if err != nil { + logrus.WithError(err).Debug("Failed to ping syncthing") + return false, fmt.Errorf("failed to ping syncthing: %w", err) + } + + return true, nil +} + +func (s *Syncthing) WaitForStartup(timeout time.Duration) error { + start := time.Now() + for { + if time.Since(start) > timeout { + logrus.WithFields(logrus.Fields{ + "name": s.Name, + "port": s.Port, + "homedir": s.HomeDirectory, + "apikey": s.ApiKey, + "config": s.Config, + }).Debugf("Timeout reached for syncthing") + return fmt.Errorf("timed out waiting for syncthing to start") + } + if ok, _ := s.Ping(); ok { + return nil + } + time.Sleep(500 * time.Millisecond) + } +} + +func (s *Syncthing) StopLocalSyncthing() { + logger := logrus.WithFields(logrus.Fields{ + "name": s.Name, + "port": s.Port, + "homedir": s.HomeDirectory, + "apikey": s.ApiKey, + "config": s.Config, + }) + if s.Cmd == nil { + logger.Error("syncthing is not running") + } + + err := s.Cmd.Process.Signal(syscall.SIGINT) + if err != nil { + logger.WithError(err).Error("failed to kill syncthing process") + } + + _, err = s.Cmd.Process.Wait() + if err != nil { + logger.WithError(err).Error("failed to kill syncthing process") + } + + if err = CleanLocalConfig(s.Name); err != nil { + logger.WithError(err).Error("failed to clean local syncthing config") + } + +} + +func (s *Syncthing) IsRunning() bool { + if s.Cmd == nil { + return false + } + return s.Cmd.Process.Signal(syscall.Signal(0)) == nil +} diff --git a/pkg/syncthing/syncthing_test.go b/pkg/syncthing/syncthing_test.go new file mode 100644 index 000000000..5357bf383 --- /dev/null +++ b/pkg/syncthing/syncthing_test.go @@ -0,0 +1,205 @@ +// Copyright 2023 The envd 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 syncthing_test + +import ( + "fmt" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/sirupsen/logrus" + + "github.com/tensorchord/envd/pkg/syncthing" + "github.com/tensorchord/envd/pkg/util/fileutil" +) + +func TestSyncthing(t *testing.T) { + logrus.SetLevel(logrus.DebugLevel) + RegisterFailHandler(Fail) + RunSpecs(t, "Syncthing Suite") +} + +var _ = Describe("Syncthing", func() { + + BeforeEach(func() { + }) + + Describe("Syncthing", func() { + It("Starts and stops syncthing", func() { + s, err := syncthing.InitializeLocalSyncthing("s1") + Expect(err).To(BeNil()) + + Expect(s.IsRunning()).To(BeTrue()) + + s.StopLocalSyncthing() + + Expect(s.IsRunning()).To(BeFalse()) + }) + }) + + Describe("Syncthing config", func() { + It("Initializes local syncthing configuration", func() { + s, err := syncthing.InitializeLocalSyncthing("s1") + Expect(err).To(BeNil()) + + Expect(s.Port).To(Equal(syncthing.DefaultLocalPort)) + Expect(s.Config.GUI.Address()).To(Equal(fmt.Sprintf("127.0.0.1:%s", syncthing.DefaultLocalPort))) + + dirExists, err := fileutil.DirExists(s.HomeDirectory) + Expect(err).To(BeNil()) + Expect(dirExists).To(BeTrue()) + + configFilePath := syncthing.GetConfigFilePath(s.HomeDirectory) + fileExists, err := fileutil.FileExists(configFilePath) + Expect(err).To(BeNil()) + Expect(fileExists).To(BeTrue()) + + s.StopLocalSyncthing() + }) + + }) + + Describe("Install", func() { + It("Installs binary in cache directory", func() { + err := syncthing.InstallSyncthing() + Expect(err).To(BeNil()) + + Expect(syncthing.IsInstalled()).To(BeTrue()) + }) + }) +}) + +var _ = Describe("Syncthing REST API operations", func() { + var s1 *syncthing.Syncthing + var s2 *syncthing.Syncthing + + BeforeEach(func() { + var err error + initConfig := syncthing.InitLocalConfig() + homeDirectory1, err := syncthing.GetHomeDirectory() + Expect(err).To(BeNil()) + + homeDirectory2, err := syncthing.GetHomeDirectory() + Expect(err).To(BeNil()) + + initConfig1 := initConfig.Copy() + initConfig1.GUI.RawAddress = fmt.Sprintf("127.0.0.1:%s", syncthing.DefaultLocalPort) + s1 = &syncthing.Syncthing{ + Config: &initConfig1, + HomeDirectory: fmt.Sprintf("%s-1", homeDirectory1), + ApiKey: syncthing.DefaultApiKey, + Name: "s1-REST", + Port: syncthing.DefaultLocalPort, + } + + initConfig2 := initConfig.Copy() + initConfig2.GUI.RawAddress = fmt.Sprintf("127.0.0.1:%s", syncthing.DefaultRemotePort) + s2 = &syncthing.Syncthing{ + Config: &initConfig2, + HomeDirectory: fmt.Sprintf("%s-2", homeDirectory2), + ApiKey: syncthing.DefaultApiKey, + Name: "s2-REST", + Port: syncthing.DefaultRemotePort, + } + + s1.Client = s1.NewClient() + s2.Client = s2.NewClient() + + err = s1.WriteLocalConfig() + Expect(err).To(BeNil()) + + err = s2.WriteLocalConfig() + Expect(err).To(BeNil()) + + err = s1.StartLocalSyncthing() + Expect(err).To(BeNil()) + + err = s2.StartLocalSyncthing() + Expect(err).To(BeNil()) + }) + + AfterEach(func() { + s1.StopLocalSyncthing() + + s2.StopLocalSyncthing() + }) + + It("Connects two local devices", func() { + err := syncthing.ConnectDevices(s1, s2) + Expect(err).To(BeNil()) + }) + +}) + +var _ = Describe("Syncthing REST API operations", func() { + var s *syncthing.Syncthing + + BeforeEach(func() { + var err error + s, err = syncthing.InitializeLocalSyncthing("s1") + Expect(err).To(BeNil()) + }) + + AfterEach(func() { + s.StopLocalSyncthing() + }) + + It("Applies syncthing configuration twice", func() { + s.Config.GUI.Debugging = false + Expect(s.Config.GUI.Debugging).To(Equal(false)) + + s.Config.GUI.Debugging = true + + ok, err := s.Ping() + Expect(err).To(BeNil()) + Expect(ok).To(BeTrue()) + + err = s.ApplyConfig() + Expect(err).To(BeNil()) + + cfg, err := s.GetConfig() + Expect(err).To(BeNil()) + Expect(cfg.GUI.Debugging).To(Equal(true)) + + s.Config.GUI.Debugging = false + err = s.ApplyConfig() + Expect(err).To(BeNil()) + + cfg, err = s.GetConfig() + Expect(err).To(BeNil()) + Expect(cfg.GUI.Debugging).To(Equal(false)) + }) + + It("Gets the most recent event", func() { + event, err := s.GetMostRecentEvent() + Expect(err).To(BeNil()) + Expect(event.Id > 0).To(BeTrue()) + }) + +}) + +var _ = Describe("Syncthing util tests", func() { + It("Parses port correctly", func() { + addr := "127.0.0.1:8386" + + port := syncthing.ParsePortFromAddress(addr) + Expect(port).To(Equal("8386")) + + addr2 := "tcp://127.0.0.1:8386" + port2 := syncthing.ParsePortFromAddress(addr2) + Expect(port2).To(Equal("8386")) + }) +}) diff --git a/e2e/v0/language/suite_test.go b/pkg/syncthing/util.go similarity index 62% rename from e2e/v0/language/suite_test.go rename to pkg/syncthing/util.go index 2d815693b..d0d93d7ea 100644 --- a/e2e/v0/language/suite_test.go +++ b/pkg/syncthing/util.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,23 +12,26 @@ // See the License for the specific language governing permissions and // limitations under the License. -package language +package syncthing import ( - "os" - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - "github.com/tensorchord/envd/pkg/version" + "net/url" + "strings" ) -func init() { - version.SetGitTagForE2ETest(os.Getenv("GIT_LATEST_TAG")) -} +func ParsePortFromAddress(addr string) string { + if strings.Contains(addr, "://") { + rawUrl, err := url.Parse(addr) + if err != nil { + return "" + } + return rawUrl.Port() + } + + splitLst := strings.Split(addr, ":") + if len(splitLst) == 2 { + return splitLst[1] + } -func TestMain(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "envd language Suite") + return "" } diff --git a/pkg/lang/frontend/starlark/v0/universe/const.go b/pkg/types/container.go similarity index 58% rename from pkg/lang/frontend/starlark/v0/universe/const.go rename to pkg/types/container.go index 7e2b74e68..cdd3f024e 100644 --- a/pkg/lang/frontend/starlark/v0/universe/const.go +++ b/pkg/types/container.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,14 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -package universe +package types -const ( - ruleBase = "base" - ruleShell = "shell" - ruleRun = "run" - ruleGitConfig = "git_config" - ruleInclude = "include" +type ContainerStatus string - GitPrefix = "git@" +const ( + StatusCreated ContainerStatus = "created" + StatusRunning ContainerStatus = "running" + StatusPaused ContainerStatus = "paused" + StatusRestarting ContainerStatus = "restarting" + StatusRemoving ContainerStatus = "removing" + StatusExited ContainerStatus = "exited" + StatusDead ContainerStatus = "dead" ) diff --git a/pkg/types/envd.go b/pkg/types/envd.go index c7e38fa0a..e456599ed 100644 --- a/pkg/types/envd.go +++ b/pkg/types/envd.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -19,7 +19,9 @@ import ( "fmt" "github.com/cockroachdb/errors" - "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/image" + dockersystem "github.com/docker/docker/api/types/system" "github.com/moby/buildkit/util/system" servertypes "github.com/tensorchord/envd-server/api/types" @@ -33,9 +35,10 @@ const ( DefaultCondaPath = "/opt/conda/envs/envd/bin:/opt/conda/bin:/home/envd/.local/bin" DefaultJuliaPath = "/usr/local/julia/bin" // image - PythonBaseImage = "ubuntu:20.04" + PythonBaseImage = "ubuntu:22.04" + EnvdStarshipImage = "tensorchord/starship:v0.0.1" // supervisor - HorustImage = "tensorchord/horust:v0.2.1" + HorustImage = "tensorchord/horust:v0.2.3" HorustServiceDir = "/etc/horust/services" HorustLogDir = "/var/log/horust" // env @@ -52,8 +55,8 @@ var BaseEnvironment = []struct { }{ {"DEBIAN_FRONTEND", "noninteractive"}, {"PATH", DefaultSystemPath}, - {"LANG", "en_US.UTF-8"}, - {"LC_ALL", "en_US.UTF-8"}, + {"LANG", "C.UTF-8"}, + {"LC_ALL", "C.UTF-8"}, } var BaseAptPackage = []string{ "bash-static", @@ -79,6 +82,7 @@ var BaseAptPackage = []string{ "make", "zsh", "locales", + "gpg", // used by r-lang } type EnvdImage struct { @@ -102,7 +106,7 @@ type EnvdManifest struct { } type EnvdInfo struct { - types.Info + dockersystem.Info } type EnvdContext struct { @@ -121,6 +125,7 @@ type Context struct { type BuilderType string const ( + BuilderTypeMoby BuilderType = "moby-worker" BuilderTypeDocker BuilderType = "docker-container" BuilderTypeNerdctl BuilderType = "nerdctl-container" BuilderTypeKubernetes BuilderType = "kube-pod" @@ -168,7 +173,7 @@ type AuthConfig struct { JWTToken string `json:"jwt_token,omitempty"` } -func NewImageFromSummary(image types.ImageSummary) (*EnvdImage, error) { +func NewImageFromSummary(image image.Summary) (*EnvdImage, error) { img := EnvdImage{ ImageMeta: servertypes.ImageMeta{ Digest: image.ID, @@ -200,7 +205,7 @@ func NewImageFromMeta(meta servertypes.ImageMeta) (*EnvdImage, error) { return &img, nil } -func NewEnvironmentFromContainer(ctr types.Container) (*EnvdEnvironment, error) { +func NewEnvironmentFromContainer(ctr container.Summary) (*EnvdEnvironment, error) { env := EnvdEnvironment{ Environment: servertypes.Environment{ Spec: servertypes.EnvironmentSpec{Image: ctr.Image}, @@ -260,15 +265,15 @@ func newManifest(labels map[string]string) (EnvdManifest, error) { return manifest, nil } -func NewDependencyFromContainerJSON(ctr types.ContainerJSON) (*Dependency, error) { +func NewDependencyFromContainerJSON(ctr container.InspectResponse) (*Dependency, error) { return NewDependencyFromLabels(ctr.Config.Labels) } -func NewDependencyFromImageSummary(img types.ImageSummary) (*Dependency, error) { +func NewDependencyFromImageSummary(img image.Summary) (*Dependency, error) { return NewDependencyFromLabels(img.Labels) } -func NewPortBindingFromContainerJSON(ctr types.ContainerJSON) ([]PortBinding, error) { +func NewPortBindingFromContainerJSON(ctr container.InspectResponse) ([]PortBinding, error) { var labels []servertypes.EnvironmentPort err := json.Unmarshal([]byte(ctr.Config.Labels[ImageLabelPorts]), &labels) if err != nil { diff --git a/pkg/types/label.go b/pkg/types/label.go index 67d4d59e5..67f4fc92e 100644 --- a/pkg/types/label.go +++ b/pkg/types/label.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -32,7 +32,9 @@ const ( ImageLabelCUDNN = "ai.tensorchord.envd.gpu.cudnn" ImageLabelContext = "ai.tensorchord.envd.build.context" ImageLabelCacheHash = "ai.tensorchord.envd.build.digest" - RuntimeGraphCode = "ai.tensorchord.envd.runtimeGraph" + ImageLabelSyntaxVer = "ai.tensorchord.envd.syntax.version" + RuntimeGraphCode = "ai.tensorchord.envd.graph.runtime" + GeneralGraphCode = "ai.tensorchord.envd.graph.general" ImageVendorEnvd = "envd" ) diff --git a/pkg/types/types_suite_test.go b/pkg/types/types_suite_test.go index 9fab48bca..9bf376f6a 100644 --- a/pkg/types/types_suite_test.go +++ b/pkg/types/types_suite_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/util/buildkitutil/buildkit.go b/pkg/util/buildkitutil/buildkit.go new file mode 100644 index 000000000..1a3b8b92d --- /dev/null +++ b/pkg/util/buildkitutil/buildkit.go @@ -0,0 +1,83 @@ +// Copyright 2023 The envd 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 buildkitutil + +import ( + "os" + "strings" + "text/template" + + "github.com/cockroachdb/errors" + + "github.com/tensorchord/envd/pkg/util/fileutil" +) + +const buildkitConfigTemplate = ` +[registry] +{{- range $registry := .Registries }} + [registry."{{ if $registry.Name }}{{ $registry.Name }}{{ else }}docker.io{{ end }}"] + {{- if $registry.UseHTTP }} + http = true + {{- end }} + {{- if $registry.Mirror }} + mirrors = ["{{ $registry.Mirror }}"] + {{- end }} + {{- if $registry.Ca }} + ca=["/etc/registry/{{ $registry.Name }}_ca.pem"] + {{- end }} + {{- if and $registry.Cert $registry.Key }} + [[registry."{{ if $registry.Name }}{{ $registry.Name }}{{ else }}docker.io{{ end }}".keypair]] + key="/etc/registry/{{ $registry.Name }}_key.pem" + cert="/etc/registry/{{ $registry.Name }}_cert.pem" + {{- end }} +{{- end }} +` + +type Registry struct { + Name string `json:"name"` + Ca string `json:"ca"` + Cert string `json:"cert"` + Key string `json:"key"` + UseHTTP bool `json:"use_http"` + Mirror string `json:"mirror"` +} + +type BuildkitConfig struct { + Registries []Registry `json:"registries"` +} + +func (c *BuildkitConfig) String() (string, error) { + tmpl, err := template.New("buildkitConfig").Parse(buildkitConfigTemplate) + if err != nil { + return "", err + } + var config strings.Builder + err = tmpl.Execute(&config, c) + return config.String(), err +} + +func (c *BuildkitConfig) Save() error { + text, err := c.String() + if err != nil { + return err + } + + path, err := fileutil.ConfigFile("buildkitd.toml") + if err != nil { + return errors.Wrap(err, "failed to get the buildkitd config file") + } + err = os.WriteFile(path, []byte(text), 0644) + return err +} diff --git a/pkg/util/buildkitutil/buildkit_test.go b/pkg/util/buildkitutil/buildkit_test.go new file mode 100644 index 000000000..1101bb2a6 --- /dev/null +++ b/pkg/util/buildkitutil/buildkit_test.go @@ -0,0 +1,127 @@ +// Copyright 2023 The envd 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 buildkitutil + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestBuildkitWithRegistry(t *testing.T) { + testCases := []struct { + bc BuildkitConfig + expected string + }{ + { + BuildkitConfig{ + Registries: []Registry{ + { + Name: "registry.example.com", + Ca: "/etc/registry/ca.pem", + Cert: "/etc/registry/cert.pem", + Key: "/etc/registry/key.pem", + UseHTTP: false, + Mirror: "https://mirror.example.com", + }, + }, + }, + ` +[registry] + [registry."registry.example.com"] + mirrors = ["https://mirror.example.com"] + ca=["/etc/registry/registry.example.com_ca.pem"] + [[registry."registry.example.com".keypair]] + key="/etc/registry/registry.example.com_key.pem" + cert="/etc/registry/registry.example.com_cert.pem" +`, + }, + { + BuildkitConfig{ + Registries: []Registry{ + { + Name: "registry.example.com", + UseHTTP: true, + Mirror: "https://mirror.example.com", + }, + { + Name: "docker.io", + Mirror: "https://mirror.example.com", + }, + }, + }, + ` +[registry] + [registry."registry.example.com"] + http = true + mirrors = ["https://mirror.example.com"] + [registry."docker.io"] + mirrors = ["https://mirror.example.com"] +`, + }, + { + BuildkitConfig{ + Registries: []Registry{}, + }, + ` +[registry] +`, + }, + { + BuildkitConfig{ + Registries: []Registry{ + { + Name: "registry1.example.com", + Ca: "/etc/registry/ca1.pem", + Cert: "/etc/registry/cert1.pem", + Key: "/etc/registry/key1.pem", + UseHTTP: true, + Mirror: "https://mirror.example.com", + }, + { + Name: "registry2.example.com", + Ca: "/etc/registry/ca2.pem", + Cert: "/etc/registry/cert2.pem", + Key: "/etc/registry/key2.pem", + Mirror: "https://mirror.example.com", + }, + }, + }, + ` +[registry] + [registry."registry1.example.com"] + http = true + mirrors = ["https://mirror.example.com"] + ca=["/etc/registry/registry1.example.com_ca.pem"] + [[registry."registry1.example.com".keypair]] + key="/etc/registry/registry1.example.com_key.pem" + cert="/etc/registry/registry1.example.com_cert.pem" + [registry."registry2.example.com"] + mirrors = ["https://mirror.example.com"] + ca=["/etc/registry/registry2.example.com_ca.pem"] + [[registry."registry2.example.com".keypair]] + key="/etc/registry/registry2.example.com_key.pem" + cert="/etc/registry/registry2.example.com_cert.pem" +`, + }, + } + + for _, tc := range testCases { + config, err := tc.bc.String() + assert.NoError(t, err) + assert.Equal(t, strings.TrimSpace(tc.expected), strings.TrimSpace(config)) + } +} diff --git a/pkg/util/envutil/env.go b/pkg/util/envutil/env.go new file mode 100644 index 000000000..1036a557c --- /dev/null +++ b/pkg/util/envutil/env.go @@ -0,0 +1,38 @@ +// Copyright 2023 The envd 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 envutil + +import ( + "os" + "time" + + log "github.com/sirupsen/logrus" +) + +// GetDurationWithDefault parses a duration string from environment variable with the given key +// or uses the given default duration. The duration string is a possibly signed sequence of +// decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". +// Valid time units are "ns", "us" (or "ยตs"), "ms", "s", "m", "h". +func GetDurationWithDefault(key string, o time.Duration) time.Duration { + v, found := os.LookupEnv(key) + if found && v != "" { + d, err := time.ParseDuration(v) + if err == nil { + return d + } + log.WithField(key, v).WithError(err).Panic("failed to parse") + } + return o +} diff --git a/e2e/v0/docs/suite_test.go b/pkg/util/envutil/env_test.go similarity index 50% rename from e2e/v0/docs/suite_test.go rename to pkg/util/envutil/env_test.go index 000291872..8cafcd5e2 100644 --- a/e2e/v0/docs/suite_test.go +++ b/pkg/util/envutil/env_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,23 +12,21 @@ // See the License for the specific language governing permissions and // limitations under the License. -package docs +package envutil import ( - "os" "testing" + "time" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - "github.com/tensorchord/envd/pkg/version" + "github.com/stretchr/testify/assert" ) -func init() { - version.SetGitTagForE2ETest(os.Getenv("GIT_LATEST_TAG")) -} - -func TestMain(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "envd cli Suite") +func TestGetDurationWithDefault(t *testing.T) { + assert.Equal(t, time.Second, GetDurationWithDefault("", time.Second), "default value") + t.Setenv("FOO", "bar") + assert.Panics(t, func() { GetDurationWithDefault("FOO", time.Second) }, "bad value") + t.Setenv("FOO", "1h") + assert.Equal(t, time.Hour, GetDurationWithDefault("FOO", time.Second), "env var value") + t.Setenv("FOO", "") + assert.Equal(t, time.Second, GetDurationWithDefault("FOO", time.Second), "empty var value; default value") } diff --git a/pkg/util/fileutil/file.go b/pkg/util/fileutil/file.go index 43e635a46..06438bfc9 100644 --- a/pkg/util/fileutil/file.go +++ b/pkg/util/fileutil/file.go @@ -27,9 +27,10 @@ import ( ) var ( - DefaultConfigDir string - DefaultCacheDir string - DefaultEnvdLibDir string + DefaultConfigDir string + DefaultCacheDir string + DefaultEnvdLibDir string + DefaultTemplateDir string ) func init() { @@ -40,6 +41,7 @@ func init() { DefaultConfigDir = filepath.Join(home, ".config", "envd") DefaultCacheDir = filepath.Join(home, ".cache", "envd") DefaultEnvdLibDir = filepath.Join(DefaultCacheDir, "envdlib") + DefaultTemplateDir = filepath.Join(DefaultConfigDir, "templates") } // FileExists returns true if the file exists @@ -98,15 +100,18 @@ func DirExists(filename string) (bool, error) { func CreateIfNotExist(f string) error { _, err := os.Stat(f) + if err == nil { + return nil + } + + if !os.IsNotExist(err) { + return errors.Wrap(err, "failed to stat file") + } + + logrus.WithField("filename", f).Debug("Creating file") + _, err = os.Create(f) if err != nil { - if os.IsNotExist(err) { - logrus.WithField("filename", f).Debug("Creating file") - if _, err := os.Create(f); err != nil { - return errors.Wrap(err, "failed to create file") - } - } else { - return errors.Wrap(err, "failed to stat file") - } + return errors.Wrap(err, "failed to create file") } return nil } @@ -133,6 +138,11 @@ func CacheFile(filename string) (string, error) { return validateAndJoin(DefaultCacheDir, filename) } +// TemplateFile returns the location for the specified envd template file +func TemplateFile(filename string) (string, error) { + return validateAndJoin(DefaultTemplateDir, filename) +} + func validateAndJoin(dir, file string) (string, error) { if strings.ContainsRune(file, os.PathSeparator) { return "", errors.Newf("filename %s should not contain any path separator", file) diff --git a/pkg/util/fileutil/file_test.go b/pkg/util/fileutil/file_test.go index 72ab5415a..853f47404 100644 --- a/pkg/util/fileutil/file_test.go +++ b/pkg/util/fileutil/file_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/util/fileutil/namegenerator.go b/pkg/util/fileutil/namegenerator.go index 04dcc7f12..5b8865770 100644 --- a/pkg/util/fileutil/namegenerator.go +++ b/pkg/util/fileutil/namegenerator.go @@ -1,5 +1,5 @@ -// Copyright 2022 The envd Authors -// Copyright 2022 The moby Authors +// Copyright 2023 The envd Authors +// Copyright 2023 The moby Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -269,7 +269,7 @@ var ( // David Lee Chaum - American computer scientist and cryptographer. Known for his seminal contributions in the field of anonymous communication. https://en.wikipedia.org/wiki/David_Chaum "chaum", - // Pafnuty Chebyshev - Russian mathematician. He is known fo his works on probability, statistics, mechanics, analytical geometry and number theory https://en.wikipedia.org/wiki/Pafnuty_Chebyshev + // Pafnuty Chebyshev - Russian mathematician. He is known for his works on probability, statistics, mechanics, analytical geometry and number theory https://en.wikipedia.org/wiki/Pafnuty_Chebyshev "chebyshev", // Joan Clarke - Bletchley Park code breaker during the Second World War who pioneered techniques that remained top secret for decades. Also an accomplished numismatist https://en.wikipedia.org/wiki/Joan_Clarke diff --git a/pkg/util/netutil/netutil.go b/pkg/util/netutil/netutil.go index 228b1057c..b8cc92d89 100644 --- a/pkg/util/netutil/netutil.go +++ b/pkg/util/netutil/netutil.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -32,13 +32,13 @@ func GetFreePort() (int, error) { // GetHost get the IP address from the address. func GetHost(addr string) (string, error) { - if u, err := url.Parse(addr); err != nil { + u, err := url.Parse(addr) + if err != nil { return "", err - } else { - h := u.Hostname() - if h == "" { - return "", fmt.Errorf("failed to get the hostname from %s", addr) - } - return h, nil } + h := u.Hostname() + if h == "" { + return "", fmt.Errorf("failed to get the hostname from %s", addr) + } + return h, nil } diff --git a/pkg/util/netutil/netutil_test.go b/pkg/util/netutil/netutil_test.go index 5a30a2c39..f575ffe0e 100644 --- a/pkg/util/netutil/netutil_test.go +++ b/pkg/util/netutil/netutil_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/util/osutil/wsl.go b/pkg/util/osutil/wsl.go index bd6e83d34..8ec897af0 100644 --- a/pkg/util/osutil/wsl.go +++ b/pkg/util/osutil/wsl.go @@ -1,5 +1,5 @@ -// Copyright 2022 The envd Authors -// Copyright 2022 mateors +// Copyright 2023 The envd Authors +// Copyright 2023 mateors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -34,7 +34,7 @@ func IsWsl() bool { cmd := exec.Command("cat", "/proc/version") output, err := cmd.Output() if err != nil { - logrus.Debugf("Error when check whether sys is WSL: %v", err) + logrus.WithError(err).Error("Error when check whether sys is WSL") return false } diff --git a/pkg/lang/frontend/starlark/v0/starlark_suite_test.go b/pkg/util/runtimeutil/runtimeutil.go similarity index 73% rename from pkg/lang/frontend/starlark/v0/starlark_suite_test.go rename to pkg/util/runtimeutil/runtimeutil.go index 75e46eea6..5e5ccfca2 100644 --- a/pkg/lang/frontend/starlark/v0/starlark_suite_test.go +++ b/pkg/util/runtimeutil/runtimeutil.go @@ -1,4 +1,5 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors +// Copyright 2023 mateors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,16 +13,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -package v0 +package runtimeutil import ( - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" + "fmt" + "runtime" ) -func TestManager(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Starlark Suite") +func GetRuntimePlatform() string { + return fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH) } diff --git a/pkg/util/starlarkutil/stringslice.go b/pkg/util/starlarkutil/stringslice.go index 5ea83cd57..a4d81bcfc 100644 --- a/pkg/util/starlarkutil/stringslice.go +++ b/pkg/util/starlarkutil/stringslice.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/util/starlarkutil/stringslice_test.go b/pkg/util/starlarkutil/stringslice_test.go index 6451dabc6..03a71b7c8 100644 --- a/pkg/util/starlarkutil/stringslice_test.go +++ b/pkg/util/starlarkutil/stringslice_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The envd Authors +// Copyright 2023 The envd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/util/ziputil/unzip.go b/pkg/util/ziputil/unzip.go index 459730ffa..25bd4a7f5 100644 --- a/pkg/util/ziputil/unzip.go +++ b/pkg/util/ziputil/unzip.go @@ -1,5 +1,5 @@ -// Copyright 2022 The envd Authors -// Copyright 2022 mateors +// Copyright 2023 The envd Authors +// Copyright 2023 mateors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pyproject.toml b/pyproject.toml index 9787c3bdf..57937ecff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,12 @@ [build-system] requires = ["setuptools", "wheel"] build-backend = "setuptools.build_meta" + +[tool.ruff] +target-version = "py312" +extend-include = ["*.envd"] +[tool.ruff.lint] +select = ["E", "F", "G", "B", "I", "SIM", "TID", "PL", "RUF"] +ignore = ["E501", "PLW2901", "F821"] +[tool.ruff.lint.pylint] +max-args = 7 diff --git a/setup.py b/setup.py index 495842103..e172c849c 100644 --- a/setup.py +++ b/setup.py @@ -12,16 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. +import logging import os -from setuptools.command.sdist import sdist -from setuptools import setup, Extension, find_packages +import subprocess + +from setuptools import Extension, find_packages, setup from setuptools.command.build_ext import build_ext +from setuptools.command.sdist import sdist from wheel.bdist_wheel import bdist_wheel - -import subprocess -import logging - with open("README.md", "r", encoding="utf-8") as f: readme = f.read() @@ -89,12 +88,9 @@ def get_version(): "Intended Audience :: Science/Research", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 3", ] @@ -112,7 +108,6 @@ def get_version(): author_email="envd-maintainers@tensorchord.ai", packages=find_packages(), include_package_data=True, - python_requires=">=3.6", data_files=[("bin", ["bin/envd"])], classifiers=classifiers, zip_safe=False, diff --git a/typos.toml b/typos.toml new file mode 100644 index 000000000..1d127d1ee --- /dev/null +++ b/typos.toml @@ -0,0 +1,8 @@ +# See https://github.com/crate-ci/typos/blob/master/docs/reference.md to configure typos +[files] +extend-exclude = ["CHANGELOG.md", "go.mod", "go.sum"] +[default.extend-words] +iterm = "iterm" +WRONLY = "WRONLY" +# this typo is introduced in the upstream library +vertexes = "vertexes"