├── .github └── workflows │ ├── actions_lint.yaml │ ├── actions_test_e2e_install.yaml │ ├── actions_test_e2e_setup-app_manual.yaml │ ├── actions_test_e2e_setup-app_preset.yaml │ ├── actions_test_unit.yaml │ ├── docs_cli_and_api_partials_checker.yml │ ├── lint.yaml │ ├── release_please.yml │ ├── tests.yml │ ├── trdl_publisher.yml │ ├── trdl_releaser.yml │ ├── website_broken_links_checker.yml │ ├── website_converge.yml │ └── website_test.yml ├── .gitignore ├── .golangci.yaml ├── .prettierignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── Taskfile.dist.yaml ├── actions ├── .env.unix-debug ├── .prettierignore ├── .prettierrc.yml ├── .yaml-lint.yml ├── README.md ├── eslint.config.mjs ├── install │ ├── action.yml │ ├── dist │ │ ├── index.mjs │ │ └── index.mjs.map │ └── src │ │ ├── action.test.ts │ │ ├── action.ts │ │ └── index.ts ├── jest.config.mjs ├── lib │ ├── exec.ts │ ├── gpg-cli.test.ts │ ├── gpg-cli.ts │ ├── trdl-cli.test.ts │ └── trdl-cli.ts ├── package-lock.json ├── package.json ├── rollup.config.mjs ├── setup-app │ ├── action.yml │ ├── dist │ │ ├── index.mjs │ │ └── index.mjs.map │ └── src │ │ ├── action.ts │ │ ├── add.test.ts │ │ ├── add.ts │ │ ├── index.ts │ │ ├── preset.test.ts │ │ ├── preset.ts │ │ ├── use.test.ts │ │ └── use.ts ├── test │ └── mocks │ │ ├── core.ts │ │ ├── fs.ts │ │ ├── gpg-cli.ts │ │ ├── io.ts │ │ ├── lib-exec.ts │ │ ├── tool-cache.ts │ │ └── trdl-cli.ts ├── tsconfig.base.json ├── tsconfig.eslint.json └── tsconfig.json ├── client ├── README.md ├── Taskfile.yaml ├── cmd │ └── trdl │ │ ├── add.go │ │ ├── bin_path.go │ │ ├── command │ │ ├── groups.go │ │ ├── md_docs.go │ │ ├── template.go │ │ └── templater.go │ │ ├── common.go │ │ ├── dir_path.go │ │ ├── docs.go │ │ ├── exec.go │ │ ├── list.go │ │ ├── main.go │ │ ├── main_test.go │ │ ├── remove.go │ │ ├── set_default_channel.go │ │ ├── update.go │ │ ├── use.go │ │ └── version.go ├── go.mod ├── go.sum ├── pkg │ ├── client │ │ ├── client.go │ │ ├── configuration.go │ │ ├── errors.go │ │ └── interface.go │ ├── logger │ │ └── log.go │ ├── repo │ │ ├── bin_path.go │ │ ├── clean_releases.go │ │ ├── client.go │ │ ├── dir_path.go │ │ ├── errors.go │ │ ├── exec_path.go │ │ ├── interface.go │ │ ├── setup.go │ │ ├── update.go │ │ └── use.go │ ├── trdl │ │ ├── const.go │ │ └── version.go │ ├── tuf │ │ ├── client.go │ │ ├── download.go │ │ └── trace.go │ └── util │ │ ├── exec_unix.go │ │ ├── exec_windows.go │ │ ├── file.go │ │ ├── hashsum.go │ │ ├── metafile.go │ │ └── path.go ├── scripts │ └── verify-dist-binaries.sh ├── trdl.yaml └── trdl_channels.yaml ├── docs ├── .gitignore ├── .helm │ ├── templates │ │ ├── 10-app.yaml │ │ ├── 14-tuf-router.yaml │ │ ├── 20-ingress-tuf-router.yaml │ │ ├── 20-ingress.yaml │ │ ├── _helpers.tpl │ │ └── _rewrites.tpl │ └── values.yaml ├── .werf │ ├── nginx-tuf-router.conf │ ├── nginx.conf │ └── tuf-router.lua ├── Gemfile ├── Gemfile.lock ├── LOCALDEV.md ├── Taskfile.yaml ├── _config.yml ├── _config_ru.yml ├── _data │ ├── breadcrumbs.yml │ ├── sidebars │ │ ├── _cli.yml │ │ ├── _reference.yml │ │ ├── _vault_plugin.yml │ │ └── reference.yml │ ├── topnav.yml │ ├── trdl.yml │ └── trdl_channels.yml ├── _includes │ ├── footer.html │ ├── head.html │ ├── index_en │ │ ├── architecture.html │ │ ├── benefits.html │ │ ├── easy-use.html │ │ ├── how-work-trdl.html │ │ ├── intro.html │ │ ├── slider-release.html │ │ ├── slider.html │ │ ├── solve-that.html │ │ └── whats-problems.html │ ├── index_ru │ │ ├── architecture.html │ │ ├── benefits.html │ │ ├── easy-use.html │ │ ├── how-work-trdl.html │ │ ├── intro.html │ │ ├── slider-release.html │ │ ├── slider.html │ │ ├── solve-that.html │ │ └── whats-problems.html │ ├── menu-burger.html │ ├── nav-breadcrumbs.html │ ├── reference │ │ ├── cli │ │ │ ├── trdl.md │ │ │ ├── trdl.short.md │ │ │ ├── trdl_.md │ │ │ ├── trdl_.short.md │ │ │ ├── trdl_add.md │ │ │ ├── trdl_add.short.md │ │ │ ├── trdl_bin_path.md │ │ │ ├── trdl_bin_path.short.md │ │ │ ├── trdl_dir_path.md │ │ │ ├── trdl_dir_path.short.md │ │ │ ├── trdl_docs.md │ │ │ ├── trdl_docs.short.md │ │ │ ├── trdl_exec.md │ │ │ ├── trdl_exec.short.md │ │ │ ├── trdl_list.md │ │ │ ├── trdl_list.short.md │ │ │ ├── trdl_remove.md │ │ │ ├── trdl_remove.short.md │ │ │ ├── trdl_set_default_channel.md │ │ │ ├── trdl_set_default_channel.short.md │ │ │ ├── trdl_update.md │ │ │ ├── trdl_update.short.md │ │ │ ├── trdl_use.md │ │ │ ├── trdl_use.short.md │ │ │ ├── trdl_version.md │ │ │ └── trdl_version.short.md │ │ ├── configuration_table_directive.html │ │ ├── trdl_channels_yaml │ │ │ └── table.html │ │ ├── trdl_yaml │ │ │ ├── example_build_sh.md.liquid │ │ │ ├── example_result.md.liquid │ │ │ ├── example_trdl_yaml.md.liquid │ │ │ ├── example_trdl_yaml_w_secrets.md.liquid │ │ │ └── table.html │ │ └── vault_plugin │ │ │ ├── configure.md │ │ │ ├── configure │ │ │ ├── build │ │ │ │ ├── secrets.md │ │ │ │ └── secrets │ │ │ │ │ └── id.md │ │ │ ├── git_credential.md │ │ │ ├── last_published_git_commit.md │ │ │ ├── pgp_signing_key.md │ │ │ ├── trusted_pgp_public_key.md │ │ │ └── trusted_pgp_public_key │ │ │ │ └── name.md │ │ │ ├── index.md │ │ │ ├── publish.md │ │ │ ├── release.md │ │ │ ├── task.md │ │ │ └── task │ │ │ ├── configure.md │ │ │ ├── uuid.md │ │ │ └── uuid │ │ │ ├── cancel.md │ │ │ └── log.md │ ├── security_en │ │ ├── components.html │ │ ├── intro.html │ │ ├── not-protecting.html │ │ ├── provide.html │ │ └── recommendations.html │ ├── security_ru │ │ ├── components.html │ │ ├── intro.html │ │ ├── not-protecting.html │ │ ├── provide.html │ │ └── recommendations.html │ ├── sidebar.html │ ├── sidebar_entry.html │ ├── toc.html │ └── topnav.html ├── _layouts │ ├── default.html │ ├── main-page.html │ ├── none.html │ ├── page-nosidebar.html │ ├── page.html │ └── sidebar.html ├── _plugins │ ├── custom_filters.rb │ ├── offtopic.rb │ ├── raise_error.rb │ └── variables.rb ├── css │ ├── bootstrap.min.css │ ├── components │ │ ├── _footer.scss │ │ ├── _header.scss │ │ ├── _menu-burger.scss │ │ ├── index │ │ │ ├── _architecture.scss │ │ │ ├── _benefits.scss │ │ │ ├── _easy-use.scss │ │ │ ├── _how-work-trdl.scss │ │ │ ├── _intro.scss │ │ │ ├── _slider.scss │ │ │ ├── _solve-that.scss │ │ │ └── _whats-problems.scss │ │ └── security │ │ │ ├── _components.scss │ │ │ ├── _intro.scss │ │ │ ├── _not-protecting.scss │ │ │ ├── _provide.scss │ │ │ └── _recommendations.scss │ ├── configuration-table.css │ ├── docs.css │ ├── featherlight.min.css │ ├── font-awesome.min.css │ ├── fonts │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ └── fontawesome-webfont.woff2 │ ├── guides.css │ ├── header.css │ ├── index.css │ ├── installation.css │ ├── introduction.css │ ├── main.scss │ ├── misc │ │ ├── _mixins.scss │ │ ├── _variables.scss │ │ └── common.scss │ ├── normalize.css │ ├── overview.css │ ├── printstyles.css │ ├── submenu.css │ ├── syntax.css │ └── tab.css ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 ├── images │ ├── 404.png │ ├── arrow.svg │ ├── backgrounds │ │ ├── community.svg │ │ ├── feature_special.svg │ │ ├── feature_special2.svg │ │ ├── features.svg │ │ ├── intro-lines.svg │ │ ├── intro.svg │ │ ├── presentation.svg │ │ ├── stats.svg │ │ └── welcome.svg │ ├── favicon │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon.png │ │ ├── browserconfig.xml │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ ├── mstile-150x150.png │ │ ├── safari-pinned-tab.svg │ │ └── site.webmanifest │ ├── icons │ │ ├── bullet.svg │ │ ├── check.svg │ │ ├── discourse.svg │ │ ├── discussions.svg │ │ ├── dropdown.svg │ │ ├── feature-ansible.svg │ │ ├── feature-config.svg │ │ ├── feature-debug.svg │ │ ├── feature-easy.svg │ │ ├── feature-helm.svg │ │ ├── feature-kubernetes.svg │ │ ├── feature-lifecycle.svg │ │ ├── feature-size.svg │ │ ├── github.svg │ │ ├── heart.png │ │ ├── heart.svg │ │ ├── rss.svg │ │ ├── search.svg │ │ ├── slack.svg │ │ ├── sprite.svg │ │ ├── star.svg │ │ ├── telegram.svg │ │ └── twitter.svg │ ├── intro-scheme_en.svg │ ├── intro-scheme_ru.svg │ ├── intro.svg │ ├── logo.svg │ ├── share.png │ ├── slider │ │ ├── publish │ │ │ ├── 1.svg │ │ │ ├── 2.svg │ │ │ ├── 3.svg │ │ │ ├── 4.svg │ │ │ ├── 5.svg │ │ │ ├── 6.svg │ │ │ └── 7.svg │ │ └── release │ │ │ ├── 1.svg │ │ │ ├── 2.svg │ │ │ ├── 3.svg │ │ │ ├── 4.svg │ │ │ ├── 5.svg │ │ │ └── 6.svg │ ├── telegram.svg │ ├── werf-logo.svg │ └── werf-schema.png ├── jekyll.Dockerfile ├── js │ ├── channels.js │ ├── customscripts.js │ ├── details.js │ ├── featherlight.min.js │ ├── installation.js │ ├── introduction.js │ ├── jekyll-search.js │ ├── jquery-3.1.0.min.js │ ├── jquery.ba-throttle-debounce.min.js │ ├── jquery.localScroll.min.js │ ├── jquery.navgoco.min.js │ ├── jquery.scrollTo.min.js │ ├── jquery.shuffle.min.js │ ├── masonry.pkgd.min.js │ ├── menu-burger.js │ ├── popup.js │ ├── slider.js │ ├── tab.js │ ├── tippy.js │ └── toc.js ├── pages_en │ ├── 404.md │ ├── QUICKSTART.md │ ├── index.html │ ├── reference │ │ ├── cli │ │ │ ├── overview.md │ │ │ ├── trdl_add.md │ │ │ ├── trdl_bin_path.md │ │ │ ├── trdl_dir_path.md │ │ │ ├── trdl_exec.md │ │ │ ├── trdl_list.md │ │ │ ├── trdl_remove.md │ │ │ ├── trdl_set_default_channel.md │ │ │ ├── trdl_update.md │ │ │ └── trdl_use.md │ │ ├── trdl_channels_yaml.md │ │ ├── trdl_yaml.md │ │ ├── tuf_repository_layout.md │ │ └── vault_plugin │ │ │ ├── configure.md │ │ │ ├── configure │ │ │ ├── build │ │ │ │ ├── secrets.md │ │ │ │ └── secrets │ │ │ │ │ └── id.md │ │ │ ├── git_credential.md │ │ │ ├── last_published_git_commit.md │ │ │ ├── pgp_signing_key.md │ │ │ ├── trusted_pgp_public_key.md │ │ │ └── trusted_pgp_public_key │ │ │ │ └── name.md │ │ │ ├── index.md │ │ │ ├── publish.md │ │ │ ├── release.md │ │ │ ├── task.md │ │ │ └── task │ │ │ ├── configure.md │ │ │ ├── uuid.md │ │ │ └── uuid │ │ │ ├── cancel.md │ │ │ └── log.md │ └── security.html ├── pages_ru │ ├── 404.md │ ├── QUICKSTART.md │ ├── index.html │ ├── reference │ │ ├── cli │ │ ├── trdl_channels_yaml.md │ │ ├── trdl_yaml.md │ │ ├── tuf_repository_layout.md │ │ └── vault_plugin │ └── security.html ├── trdl-client.asc ├── trdl-server.asc └── werf.yaml ├── e2e ├── Taskfile.yaml ├── go.mod ├── go.sum └── tests │ ├── client │ ├── _fixtures │ │ └── tuf_repo │ │ │ ├── Dockerfile │ │ │ ├── docker-compose.yaml │ │ │ └── staged │ │ │ └── targets │ │ │ ├── channels │ │ │ └── 0 │ │ │ │ ├── alpha │ │ │ │ ├── beta │ │ │ │ ├── ea │ │ │ │ ├── rock-solid │ │ │ │ └── stable │ │ │ └── releases │ │ │ ├── v0.0.1 │ │ │ ├── any-any │ │ │ │ └── bin │ │ │ │ │ └── script.sh │ │ │ └── windows-any │ │ │ │ └── bin │ │ │ │ └── script.bat │ │ │ └── v0.0.2 │ │ │ ├── any-any │ │ │ └── bin │ │ │ │ └── script.sh │ │ │ └── windows-any │ │ │ └── bin │ │ │ └── script.bat │ ├── basic_test.go │ ├── suite_test.go │ └── use_test.go │ └── flow │ ├── _fixtures │ ├── complete_cycle │ │ ├── docker-compose.yaml │ │ └── trdl.yaml │ └── pgp_keys │ ├── complete_cycle_test.go │ └── suite_test.go ├── release ├── Taskfile.yaml ├── cmd │ └── trdl-vault │ │ ├── commands │ │ └── commands.go │ │ ├── common │ │ ├── cmd_data.go │ │ └── common.go │ │ └── main.go ├── go.mod ├── go.sum ├── pkg │ ├── client │ │ └── main.go │ └── vault │ │ ├── client.go │ │ └── error.go ├── scripts │ └── verify-dist-binaries.sh ├── trdl.yaml └── trdl_channels.yaml ├── server ├── .gitignore ├── Dockerfile ├── README.md ├── Taskfile.yaml ├── backend.go ├── backend_test.go ├── cmd │ ├── vault-plugin-docs │ │ └── main.go │ └── vault-plugin-secrets-trdl │ │ └── main.go ├── examples │ ├── publish.sh │ └── release.sh ├── go.mod ├── go.sum ├── path_configure.go ├── path_configure_test.go ├── path_publish.go ├── path_publish_test.go ├── path_release.go ├── path_release_test.go ├── periodic.go ├── pkg │ ├── config │ │ ├── trdl.go │ │ └── trdl_channels.go │ ├── docker │ │ ├── build.go │ │ ├── builder.go │ │ ├── dockerfile.go │ │ ├── secrets.go │ │ ├── util.go │ │ └── util_test.go │ ├── gendocs │ │ ├── gendocs.go │ │ ├── generator.go │ │ ├── jekyll_pages_generator.go │ │ ├── jekyll_sidebar.go │ │ ├── markdown_pages_generator.go │ │ ├── templates.go │ │ └── util.go │ ├── git │ │ ├── _fixtures │ │ │ └── pgp_keys │ │ │ │ ├── developer_private.pgp │ │ │ │ ├── developer_public.pgp │ │ │ │ ├── pm_private.pgp │ │ │ │ ├── pm_public.pgp │ │ │ │ ├── tl_private.pgp │ │ │ │ └── tl_public.pgp │ │ ├── credentials.go │ │ ├── credentials_test.go │ │ ├── repository.go │ │ ├── signatures.go │ │ ├── signatures_loader_test.go │ │ ├── signatures_test.go │ │ ├── suite_test.go │ │ ├── utils.go │ │ └── utils_test.go │ ├── keyhelper │ │ └── load_keys.go │ ├── pgp │ │ ├── backend.go │ │ ├── backend_test.go │ │ ├── rsa_signing_key.go │ │ ├── rsa_signing_key_test.go │ │ ├── storage.go │ │ └── util.go │ ├── publisher │ │ ├── backend.go │ │ ├── filesystem.go │ │ ├── interface.go │ │ ├── non_atomic_tuf_store.go │ │ ├── periodic.go │ │ ├── publisher.go │ │ ├── repository.go │ │ ├── s3_filesystem.go │ │ ├── suite_test.go │ │ ├── tuf_repo_priv_keys.go │ │ ├── tuf_repo_rotator.go │ │ └── tuf_repo_rotator_test.go │ ├── secrets │ │ ├── backend.go │ │ └── storage.go │ ├── tasks_manager │ │ ├── actions.go │ │ ├── actions_test.go │ │ ├── backend.go │ │ ├── backend_test.go │ │ ├── config.go │ │ ├── config_storage.go │ │ ├── interface.go │ │ ├── manager.go │ │ ├── manager_test.go │ │ ├── periodic.go │ │ ├── periodic_test.go │ │ ├── task.go │ │ ├── testutil │ │ │ └── paths.go │ │ └── worker │ │ │ ├── buffer.go │ │ │ ├── interface.go │ │ │ ├── job.go │ │ │ ├── worker.go │ │ │ └── worker_test.go │ ├── testutil │ │ ├── command.go │ │ ├── file.go │ │ ├── git.go │ │ ├── random.go │ │ └── suite.go │ └── util │ │ ├── clock.go │ │ ├── io.go │ │ ├── logical_error.go │ │ ├── request.go │ │ ├── throughput_io.go │ │ └── utils.go ├── scripts │ └── verify-dist-binaries.sh ├── trdl.yaml └── trdl_channels.yaml └── trdl-builder.Dockerfile /.github/workflows/actions_lint.yaml: -------------------------------------------------------------------------------- 1 | name: Actions. Lint 2 | 3 | on: [push] 4 | 5 | jobs: 6 | lint: 7 | runs-on: ubuntu-22.04 8 | timeout-minutes: 10 9 | 10 | steps: 11 | - name: Checkout code 12 | uses: actions/checkout@v4 13 | 14 | - name: Use Node.js 15 | uses: actions/setup-node@v4 16 | with: 17 | node-version: '18.x' 18 | 19 | - name: Clean install a project 20 | working-directory: actions 21 | run: npm ci 22 | 23 | - name: Lint 24 | working-directory: actions 25 | run: | 26 | npm run lint 27 | npm run format:check 28 | -------------------------------------------------------------------------------- /.github/workflows/actions_test_e2e_install.yaml: -------------------------------------------------------------------------------- 1 | name: Actions. Test e2e install 2 | 3 | on: [push] 4 | 5 | jobs: 6 | test-e2e-install: 7 | runs-on: ubuntu-22.04 8 | timeout-minutes: 10 9 | 10 | steps: 11 | - name: Checkout code 12 | uses: actions/checkout@v4 13 | 14 | - name: Install trdl 15 | uses: ./actions/install 16 | 17 | - name: Use trdl binary 18 | run: | 19 | trdl --help 20 | -------------------------------------------------------------------------------- /.github/workflows/actions_test_e2e_setup-app_manual.yaml: -------------------------------------------------------------------------------- 1 | name: Actions. Test e2e setup-app manually 2 | 3 | on: [push] 4 | 5 | jobs: 6 | test-e2e-setup-app-manually: 7 | runs-on: ubuntu-22.04 8 | timeout-minutes: 10 9 | 10 | steps: 11 | - name: Checkout code 12 | uses: actions/checkout@v4 13 | 14 | - name: Setup application 15 | uses: ./actions/setup-app 16 | with: 17 | repo: kubedog 18 | url: https://tuf.kubedog.werf.io 19 | root-version: 12 20 | root-sha512: 6462a80292eb6d7712d8a18126366511f9c47a566f121a7745cfd68b624dc340b6591c2cadfe20690eb38296c399a3f4e6948aca90be60e446ed05c3c238294c 21 | group: 0 22 | # channel: stable # optional param 23 | 24 | - name: Use kubedog binary 25 | run: | 26 | kubedog --help 27 | -------------------------------------------------------------------------------- /.github/workflows/actions_test_e2e_setup-app_preset.yaml: -------------------------------------------------------------------------------- 1 | name: Actions. Test e2e setup-app preset 2 | 3 | on: [push] 4 | 5 | jobs: 6 | test-e2e-setup-app-preset: 7 | runs-on: ubuntu-22.04 8 | timeout-minutes: 10 9 | 10 | strategy: 11 | matrix: 12 | app: [werf, nelm, kubedog] 13 | 14 | steps: 15 | - name: Checkout code 16 | uses: actions/checkout@v4 17 | 18 | - name: Setup application 19 | uses: ./actions/setup-app 20 | with: 21 | preset: ${{ matrix.app }} 22 | 23 | - name: Use ${{ matrix.app }} binary 24 | run: ${{ matrix.app }} version 25 | -------------------------------------------------------------------------------- /.github/workflows/actions_test_unit.yaml: -------------------------------------------------------------------------------- 1 | name: Actions. Test Unit 2 | 3 | on: [push] 4 | 5 | jobs: 6 | test-unit: 7 | runs-on: ubuntu-22.04 8 | timeout-minutes: 10 9 | 10 | steps: 11 | - name: Checkout code 12 | uses: actions/checkout@v4 13 | 14 | - name: Use Node.js 15 | uses: actions/setup-node@v4 16 | with: 17 | node-version: '18.x' 18 | 19 | - name: Clean install a project 20 | working-directory: actions 21 | run: npm ci 22 | 23 | - name: Test Unit 24 | working-directory: actions 25 | run: npm run test 26 | -------------------------------------------------------------------------------- /.github/workflows/docs_cli_and_api_partials_checker.yml: -------------------------------------------------------------------------------- 1 | name: CLI and API partials checker 2 | on: 3 | push: 4 | paths: 5 | - ".github/workflows/docs_cli_and_api_partials_checker.yml" 6 | - "client/cmd/**" 7 | - "server/**" 8 | workflow_dispatch: 9 | 10 | jobs: 11 | check: 12 | name: Partials checker 13 | runs-on: ubuntu-22.04 14 | steps: 15 | - name: Checkout code 16 | uses: actions/checkout@v4 17 | 18 | - name: Set up Go 19 | uses: actions/setup-go@v5 20 | with: 21 | go-version-file: server/go.mod 22 | 23 | - name: Install Task 24 | uses: arduino/setup-task@v2 25 | with: 26 | repo-token: ${{ secrets.GITHUB_TOKEN }} 27 | 28 | - name: Set up git config 29 | run: task ci:setup:git-config 30 | 31 | - name: Regen partials 32 | run: task docs:gen 33 | -------------------------------------------------------------------------------- /.github/workflows/lint.yaml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | on: 3 | push: 4 | branches: [main] 5 | paths: 6 | - "Taskfile.dist.yaml" 7 | - ".github/**" 8 | - "docs/**" 9 | - "client/**" 10 | - "e2e/**" 11 | - "server/**" 12 | pull_request: 13 | repository_dispatch: 14 | types: ["Lint"] 15 | workflow_dispatch: 16 | 17 | jobs: 18 | lint: 19 | runs-on: ubuntu-22.04 20 | timeout-minutes: 30 21 | steps: 22 | - name: Checkout code 23 | uses: actions/checkout@v4 24 | 25 | - name: Set up Go 26 | uses: actions/setup-go@v5 27 | with: 28 | go-version: "1.23" 29 | 30 | - name: Install Task 31 | uses: arduino/setup-task@v2 32 | with: 33 | repo-token: ${{ secrets.GITHUB_TOKEN }} 34 | 35 | - name: Install golangci-lint 36 | run: task -p deps:install:golangci-lint 37 | 38 | - name: Lint 39 | run: task lint 40 | 41 | notification: 42 | needs: 43 | - lint 44 | name: Notification 45 | if: always() 46 | uses: werf/common-ci/.github/workflows/notification.yml@main 47 | secrets: 48 | loopNotificationGroup: ${{ secrets.LOOP_NOTIFICATION_GROUP }} 49 | webhook: ${{ secrets.LOOP_NOTIFICATION_WEBHOOK }} 50 | notificationChannel: ${{ secrets.LOOP_NOTIFICATION_CHANNEL }} 51 | -------------------------------------------------------------------------------- /.github/workflows/website_broken_links_checker.yml: -------------------------------------------------------------------------------- 1 | name: Broken links checker 2 | on: 3 | push: 4 | branches: [main] 5 | paths: 6 | - docs 7 | schedule: 8 | - cron: "0 8 * * *" 9 | pull_request: 10 | repository_dispatch: 11 | types: [check-broken-links] 12 | workflow_dispatch: 13 | 14 | jobs: 15 | check_links: 16 | name: Links checker 17 | runs-on: ubuntu-22.04 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | lang: [ru, en] 22 | steps: 23 | - name: Checkout code 24 | uses: actions/checkout@v4 25 | 26 | - name: Install Task 27 | uses: arduino/setup-task@v2 28 | with: 29 | repo-token: ${{ secrets.GITHUB_TOKEN }} 30 | 31 | - name: Check ru broken links 32 | if: matrix.lang == 'ru' 33 | run: task docs:check-broken-links 34 | 35 | - name: Check en broken links 36 | if: matrix.lang == 'en' 37 | run: task docs:check-broken-links language=en 38 | 39 | notification: 40 | name: Notification 41 | if: always() 42 | needs: check_links 43 | uses: werf/common-ci/.github/workflows/notification.yml@main 44 | secrets: 45 | loopNotificationGroup: ${{ secrets.LOOP_NOTIFICATION_GROUP }} 46 | webhook: ${{ secrets.LOOP_NOTIFICATION_WEBHOOK }} 47 | notificationChannel: ${{ secrets.LOOP_NOTIFICATION_CHANNEL }} 48 | -------------------------------------------------------------------------------- /.github/workflows/website_converge.yml: -------------------------------------------------------------------------------- 1 | name: Converge website 2 | on: 3 | push: 4 | branches: [main] 5 | paths: 6 | - ".github/workflows/website_converge.yml" 7 | - "docs/**" 8 | workflow_dispatch: 9 | 10 | env: 11 | WERF_REPO: "ghcr.io/${{ github.repository_owner }}/trdl" 12 | 13 | jobs: 14 | converge: 15 | name: Converge site to Production 16 | runs-on: ubuntu-22.04 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v4 20 | with: 21 | fetch-depth: 0 22 | 23 | - name: Install werf 24 | uses: werf/actions/install@v2 25 | 26 | - name: Converge 27 | run: | 28 | . $(werf ci-env github --as-file) 29 | werf converge 30 | env: 31 | WERF_DIR: "docs" 32 | WERF_ENV: "production" 33 | WERF_KUBE_CONFIG_BASE64: ${{ secrets.KUBECONFIG_BASE64_PROD }} 34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | 36 | notification: 37 | name: Notification 38 | if: always() 39 | needs: converge 40 | uses: werf/common-ci/.github/workflows/notification.yml@main 41 | secrets: 42 | loopNotificationGroup: ${{ secrets.LOOP_NOTIFICATION_GROUP }} 43 | webhook: ${{ secrets.LOOP_NOTIFICATION_WEBHOOK }} 44 | notificationChannel: ${{ secrets.LOOP_NOTIFICATION_CHANNEL }} 45 | -------------------------------------------------------------------------------- /.github/workflows/website_test.yml: -------------------------------------------------------------------------------- 1 | name: Test website 2 | on: 3 | pull_request: 4 | types: [labeled, synchronize] 5 | workflow_dispatch: 6 | 7 | env: 8 | WERF_REPO: "ghcr.io/${{ github.repository_owner }}/trdl" 9 | 10 | jobs: 11 | converge: 12 | name: Converge to Test 13 | runs-on: ubuntu-22.04 14 | if: github.event.label.name == 'test docs' || contains( github.event.pull_request.labels.*.name, 'test docs' ) 15 | steps: 16 | - name: Checkout code 17 | uses: actions/checkout@v4 18 | with: 19 | fetch-depth: 0 20 | 21 | - name: Install werf 22 | uses: werf/actions/install@v2 23 | 24 | - name: Converge 25 | run: | 26 | . $(werf ci-env github --as-file) 27 | werf converge 28 | env: 29 | WERF_DIR: "docs" 30 | WERF_ENV: "test" 31 | WERF_KUBE_CONFIG_BASE64: ${{ secrets.KUBECONFIG_BASE64_DEV }} 32 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # IDE 9 | .vscode 10 | .idea 11 | 12 | # Test binary, built with `go test -c` 13 | *.test 14 | bin/ 15 | build/ 16 | dist/ 17 | 18 | # actions 19 | actions/node_modules/ 20 | actions/coverage 21 | !actions/*/dist/ 22 | 23 | # Output of the go coverage tool, specifically when used with LiteIDE 24 | *.out 25 | 26 | # Release build artifacts 27 | /client/dist 28 | /server/dist 29 | 30 | # Local dev 31 | .task 32 | tests_coverage/ 33 | .jekyll-cache/ -------------------------------------------------------------------------------- /.golangci.yaml: -------------------------------------------------------------------------------- 1 | run: 2 | timeout: 10m 3 | 4 | linters-settings: 5 | gofumpt: 6 | extra-rules: true 7 | gci: 8 | sections: 9 | - standard 10 | - default 11 | - prefix(github.com/werf/) 12 | gocritic: 13 | disabled-checks: 14 | - ifElseChain 15 | errorlint: 16 | comparison: false 17 | asserts: false 18 | misspell: 19 | locale: US 20 | 21 | linters: 22 | disable-all: true 23 | enable: 24 | # Default linters. 25 | - ineffassign 26 | - typecheck 27 | - unused 28 | 29 | # Extra linters. 30 | - asciicheck 31 | - bidichk 32 | - bodyclose 33 | - errname 34 | - errorlint 35 | - exportloopref 36 | - gci 37 | - gocritic 38 | - gofumpt 39 | - misspell 40 | - nolintlint 41 | 42 | issues: 43 | # Show all errors. 44 | max-issues-per-linter: 0 45 | max-same-issues: 0 46 | exclude-dirs: 47 | - scripts 48 | - examples 49 | - vault 50 | 51 | exclude: 52 | # TODO use %w in the future. 53 | - "non-wrapping format verb for fmt.Errorf" # errorlint 54 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .helm 2 | _data/sidebars 3 | werf.yaml 4 | _data/breadcrumbs.yml -------------------------------------------------------------------------------- /actions/.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .licenses/ 3 | dist/ 4 | node_modules/ 5 | coverage/ 6 | README.md 7 | rollup.config.mjs 8 | -------------------------------------------------------------------------------- /actions/.prettierrc.yml: -------------------------------------------------------------------------------- 1 | # See: https://prettier.io/docs/en/configuration 2 | 3 | printWidth: 120 4 | tabWidth: 2 5 | useTabs: false 6 | semi: false 7 | singleQuote: true 8 | quoteProps: as-needed 9 | jsxSingleQuote: false 10 | trailingComma: none 11 | bracketSpacing: true 12 | bracketSameLine: true 13 | arrowParens: always 14 | proseWrap: always 15 | htmlWhitespaceSensitivity: css 16 | endOfLine: lf 17 | -------------------------------------------------------------------------------- /actions/.yaml-lint.yml: -------------------------------------------------------------------------------- 1 | # See: https://yamllint.readthedocs.io/en/stable/ 2 | 3 | rules: 4 | document-end: disable 5 | document-start: 6 | level: warning 7 | present: false 8 | line-length: 9 | level: warning 10 | max: 120 11 | allow-non-breakable-words: true 12 | allow-non-breakable-inline-mappings: true 13 | ignore: 14 | - .licenses/ 15 | -------------------------------------------------------------------------------- /actions/install/action.yml: -------------------------------------------------------------------------------- 1 | name: Install trdl CLI 2 | author: 'Flant' 3 | description: 'Install trdl CLI' 4 | branding: 5 | color: blue 6 | icon: anchor 7 | inputs: 8 | channel: 9 | description: 'The one of the following channel: alpha, stable' 10 | default: 'stable' 11 | required: false 12 | version: 13 | description: 'The certain version' 14 | required: false 15 | runs: 16 | using: 'node20' 17 | main: 'dist/index.mjs' 18 | -------------------------------------------------------------------------------- /actions/install/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The entrypoint for the action. This file simply imports and runs the action. 3 | */ 4 | import { setFailed } from '@actions/core' 5 | import { Run } from './action' 6 | 7 | Run().catch(setFailed) 8 | -------------------------------------------------------------------------------- /actions/jest.config.mjs: -------------------------------------------------------------------------------- 1 | // See: https://jestjs.io/docs/configuration 2 | 3 | /** @type {import('ts-jest').JestConfigWithTsJest} **/ 4 | export default { 5 | clearMocks: true, 6 | collectCoverage: true, 7 | collectCoverageFrom: ['./*/src/*', './lib/*'], 8 | coverageDirectory: './coverage', 9 | coveragePathIgnorePatterns: ['/node_modules/', '/dist/'], 10 | coverageReporters: ['json-summary', 'text', 'lcov'], 11 | // Uncomment the below lines if you would like to enforce a coverage threshold 12 | // for your action. This will fail the build if the coverage is below the 13 | // specified thresholds. 14 | // coverageThreshold: { 15 | // global: { 16 | // branches: 100, 17 | // functions: 100, 18 | // lines: 100, 19 | // statements: 100 20 | // } 21 | // }, 22 | extensionsToTreatAsEsm: ['.ts'], 23 | moduleFileExtensions: ['ts', 'js'], 24 | preset: 'ts-jest', 25 | reporters: ['default'], 26 | resolver: 'ts-jest-resolver', 27 | restoreMocks: true, 28 | testEnvironment: 'node', 29 | testMatch: ['**/*.test.ts'], 30 | testPathIgnorePatterns: ['/dist/', '/node_modules/'], 31 | transform: { 32 | '^.+\\.ts$': [ 33 | 'ts-jest', 34 | { 35 | tsconfig: 'tsconfig.eslint.json', 36 | useESM: true 37 | } 38 | ] 39 | }, 40 | verbose: true 41 | } 42 | -------------------------------------------------------------------------------- /actions/lib/exec.ts: -------------------------------------------------------------------------------- 1 | import { exec, ExecOptions } from '@actions/exec' 2 | 3 | export interface ExecOutputResult { 4 | stdout: string[] 5 | stderr: string[] 6 | exitCode: number 7 | } 8 | 9 | export async function execOutput( 10 | commandLine: string, 11 | args?: string[], 12 | options?: ExecOptions 13 | ): Promise { 14 | const stdout: string[] = [] 15 | const stderr: string[] = [] 16 | 17 | const defaultOptions = { 18 | // https://github.com/actions/toolkit/blob/%40actions/exec%401.0.1/packages/exec/src/interfaces.ts#L39 19 | silent: false, 20 | failOnStdErr: true, 21 | listeners: { 22 | stdline(data: string) { 23 | stdout.push(data) 24 | }, 25 | errline(data: string) { 26 | stderr.push(data) 27 | } 28 | } 29 | } 30 | 31 | const exitCode = await exec(commandLine, args, { ...defaultOptions, ...options }) 32 | 33 | return { 34 | stdout, 35 | stderr, 36 | exitCode 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /actions/lib/gpg-cli.ts: -------------------------------------------------------------------------------- 1 | import { execOutput } from './exec' 2 | 3 | export class GpgCli { 4 | readonly name: string 5 | 6 | constructor() { 7 | this.name = 'gpg' 8 | } 9 | 10 | async mustGnuGP(): Promise { 11 | const help = await this.help() 12 | if (!help.includes('GnuPG')) { 13 | throw new Error('gpg is not GnuPG. Please install GnuPG') 14 | } 15 | } 16 | 17 | async import(ascPath: string): Promise { 18 | await execOutput(this.name, ['--import', ascPath], { failOnStdErr: false }) 19 | } 20 | 21 | async verify(sigPath: string, binPath: string): Promise { 22 | await execOutput(this.name, ['--verify', sigPath, binPath], { failOnStdErr: false }) 23 | } 24 | 25 | async help(): Promise { 26 | const { stdout } = await execOutput(this.name, ['--help'], { silent: true }) 27 | return stdout.join('\n') 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /actions/rollup.config.mjs: -------------------------------------------------------------------------------- 1 | // See: https://rollupjs.org/introduction/ 2 | 3 | import path from 'node:path' 4 | import commonjs from '@rollup/plugin-commonjs' 5 | import nodeResolve from '@rollup/plugin-node-resolve' 6 | import typescript from '@rollup/plugin-typescript' 7 | 8 | export default [ 9 | // prettier-ignore 10 | buildActionConfig('install'), 11 | buildActionConfig('setup-app'), 12 | ] 13 | 14 | function buildActionConfig(action) { 15 | return { 16 | input: path.join(action, 'src/index.ts'), 17 | output: { 18 | esModule: true, 19 | file: path.join(action, 'dist/index.mjs'), 20 | format: 'es', 21 | sourcemap: true 22 | }, 23 | plugins: [ 24 | typescript({ 25 | compilerOptions: { 26 | // IMPORTANT NOTE 27 | // tsconfig.base.json configured to use >= Node16 28 | // https://github.com/tsconfig/bases?tab=readme-ov-file#table-of-tsconfigs 29 | // --------------- 30 | // Suppress warning: TS5110: Option 'module' must be set to 'Node16' when option 'moduleResolution' is set to 'Node16' 31 | // https://www.typescriptlang.org/tsconfig/#moduleResolution 32 | moduleResolution: 'bundler', 33 | // --------------- 34 | outDir: path.join(action, 'dist') 35 | } 36 | }), 37 | nodeResolve({ preferBuiltins: true }), 38 | commonjs() 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /actions/setup-app/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup application via trdl 2 | author: 'Flant' 3 | description: 'Setup application via preset of trdl-actions or manually passing another arguments' 4 | branding: 5 | color: blue 6 | icon: anchor 7 | inputs: 8 | preset: 9 | description: 10 | 'The certain repository preset. It will be translated into "trdl add", "trdl update" and "trdl bin-path" 11 | arguments. For example: werf' 12 | required: false 13 | force: 14 | description: 'Perform operation anyway if set to true. For both "trdl add" and "trdl update".' 15 | required: false 16 | default: 'true' 17 | repo: 18 | description: 19 | 'The certain repository name. Required if present not given. For "trdl add", "trdl update" and "trdl bin-path".' 20 | required: true 21 | url: 22 | description: 'The certain repository url. Required if present not given. For "trdl add"' 23 | required: true 24 | root-version: 25 | description: 'The certain repository root version. Required if present not given. For "trdl add"' 26 | required: false 27 | root-sha512: 28 | description: 'The certain repository root sha512. Required if present not given. For "trdl add"' 29 | required: false 30 | group: 31 | description: 'The certain group. Required if present not given. For both "trdl update" and "trdl bin-path"' 32 | required: true 33 | channel: 34 | description: 'The one from allowed channels. For both "trdl update" and "trdl bin-path"' 35 | required: false 36 | runs: 37 | using: 'node20' 38 | main: 'dist/index.mjs' 39 | -------------------------------------------------------------------------------- /actions/setup-app/src/action.ts: -------------------------------------------------------------------------------- 1 | import { parsePresetInput } from './preset' 2 | import { TrdlCli } from '../../lib/trdl-cli' 3 | import { GpgCli } from '../../lib/gpg-cli' 4 | import { Do as DoInstall } from '../../install/src/action' 5 | import { Do as DoAdd } from './add' 6 | import { Do as DoUse } from './use' 7 | 8 | export async function Run(): Promise { 9 | const p = parsePresetInput() 10 | const trdlCli = new TrdlCli() 11 | const gpgCli = new GpgCli() 12 | 13 | await DoInstall(trdlCli, gpgCli, {}) 14 | await DoAdd(trdlCli, p) 15 | await DoUse(trdlCli, p) 16 | } 17 | -------------------------------------------------------------------------------- /actions/setup-app/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The entrypoint for the action. This file simply imports and runs the action. 3 | */ 4 | import { setFailed } from '@actions/core' 5 | import { Run } from './action' 6 | 7 | Run().catch(setFailed) 8 | -------------------------------------------------------------------------------- /actions/setup-app/src/preset.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, jest, it } from '@jest/globals' 2 | import * as core from '../../test/mocks/core' 3 | 4 | // Mocks should be declared before the module being tested is imported. 5 | jest.unstable_mockModule('@actions/core', () => core) 6 | 7 | // The module being tested should be imported dynamically. This ensures that the 8 | // mocks are used in place of any actual dependencies. 9 | const { preset, parsePresetInput } = await import('./preset') 10 | 11 | describe('setup-app/src/preset.ts', function () { 12 | describe('parsePresetInput', function () { 13 | it('should return unknown if preset not passed', function () { 14 | core.getInput.mockReturnValueOnce('') 15 | 16 | const p = parsePresetInput() 17 | expect(p).toEqual(preset.unknown) 18 | }) 19 | it('should throw err if preset is passed but not in enum', function () { 20 | const p = 'some' 21 | core.getInput.mockReturnValueOnce(p) 22 | expect(parsePresetInput).toThrow( 23 | `preset "${p}" not found. Available presets: ${Object.values(preset).join(', ')}` 24 | ) 25 | }) 26 | it('should return preset otherwise', function () { 27 | const p = 'werf' 28 | core.getInput.mockReturnValueOnce(p) 29 | expect(parsePresetInput()).toEqual(p) 30 | }) 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /actions/test/mocks/core.ts: -------------------------------------------------------------------------------- 1 | import { jest } from '@jest/globals' 2 | import type * as core from '@actions/core' 3 | 4 | export const debug = jest.fn() 5 | // export const error = jest.fn() 6 | export const info = jest.fn() 7 | export const getInput = jest.fn() 8 | export const getBooleanInput = jest.fn() 9 | // export const setOutput = jest.fn() 10 | export const setFailed = jest.fn() 11 | export const addPath = jest.fn() 12 | export const startGroup = jest.fn() 13 | export const endGroup = jest.fn() 14 | export const exportVariable = jest.fn() 15 | export const isDebug = jest.fn() 16 | export const setCommandEcho = jest.fn() 17 | 18 | export const platform = Object.create( 19 | {}, 20 | { 21 | platform: { 22 | get: jest.fn().mockReturnValue('linux') 23 | }, 24 | arch: { 25 | get: jest.fn().mockReturnValue('x64') 26 | }, 27 | isWindows: { 28 | get: jest.fn().mockReturnValue(false) 29 | } 30 | } 31 | ) 32 | -------------------------------------------------------------------------------- /actions/test/mocks/fs.ts: -------------------------------------------------------------------------------- 1 | import { jest } from '@jest/globals' 2 | import type * as fs from 'node:fs' 3 | 4 | export const chmodSync = jest.fn() 5 | -------------------------------------------------------------------------------- /actions/test/mocks/gpg-cli.ts: -------------------------------------------------------------------------------- 1 | import { GpgCli } from '../../lib/gpg-cli' 2 | import { jest } from '@jest/globals' 3 | 4 | // instance 5 | const cli = new GpgCli() 6 | 7 | export const gpgCli = { 8 | name: cli.name, 9 | mustGnuGP: jest.fn(), 10 | import: jest.fn(), 11 | verify: jest.fn(), 12 | help: jest.fn() 13 | } 14 | -------------------------------------------------------------------------------- /actions/test/mocks/io.ts: -------------------------------------------------------------------------------- 1 | import { jest } from '@jest/globals' 2 | import type * as io from '@actions/io' 3 | 4 | export const which = jest.fn() 5 | -------------------------------------------------------------------------------- /actions/test/mocks/lib-exec.ts: -------------------------------------------------------------------------------- 1 | import { jest } from '@jest/globals' 2 | import type * as lib from '../../lib/exec' 3 | 4 | export const execOutput = jest.fn() 5 | -------------------------------------------------------------------------------- /actions/test/mocks/tool-cache.ts: -------------------------------------------------------------------------------- 1 | import { jest } from '@jest/globals' 2 | import type * as toolCache from '@actions/tool-cache' 3 | 4 | export const downloadTool = jest.fn() 5 | export const find = jest.fn() 6 | export const cacheFile = jest.fn() 7 | -------------------------------------------------------------------------------- /actions/test/mocks/trdl-cli.ts: -------------------------------------------------------------------------------- 1 | import { TrdlCli } from '../../lib/trdl-cli' 2 | import { jest } from '@jest/globals' 3 | 4 | // instance 5 | const cli = new TrdlCli() 6 | 7 | export const trdlCli = { 8 | name: cli.name, 9 | defaults: jest.fn(), 10 | mustExist: jest.fn(), 11 | add: jest.fn(), 12 | remove: jest.fn(), 13 | update: jest.fn(), 14 | binPath: jest.fn(), 15 | list: jest.fn(), 16 | version: jest.fn() 17 | } 18 | -------------------------------------------------------------------------------- /actions/tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "compilerOptions": { 4 | "allowSyntheticDefaultImports": true, 5 | "declaration": false, 6 | "declarationMap": false, 7 | "esModuleInterop": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "lib": ["ES2020"], 10 | "module": "Node16", 11 | "moduleResolution": "Node16", 12 | "newLine": "lf", 13 | "noImplicitAny": true, 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": false, 16 | "pretty": true, 17 | "resolveJsonModule": true, 18 | "strict": true, 19 | "strictNullChecks": true, 20 | "target": "ES2022" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /actions/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "./tsconfig.base.json", 4 | "compilerOptions": { 5 | "allowJs": true, 6 | "noEmit": true 7 | }, 8 | "exclude": ["*/dist", "node_modules"], 9 | "include": ["test/mocks", "*/src", "lib", "eslint.config.mjs", "jest.config.mjs", "rollup.config.mjs"] 10 | } 11 | -------------------------------------------------------------------------------- /actions/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "./tsconfig.base.json", 4 | "exclude": ["*/src/*.test.ts", "test/mocks", "coverage", "*/dist", "node_modules"], 5 | "include": ["*/src", "lib"] 6 | } 7 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # trdl 2 | 3 | ## File structure 4 | 5 | ```shell 6 | ~/.trdl$ tree -a 7 | . 8 | ├── config.yaml 9 | ├── .locks 10 | │ ├── ... 11 | │ └── repositories 12 | │ ├── ... 13 | │ └── 14 | ├── logs 15 | │ ├── ... 16 | │ └── repositories 17 | │ ├── ... 18 | │ └── 19 | ├── .tmp 20 | │ ├── ... 21 | │ └── repositories 22 | │ ├── ... 23 | │ └── 24 | └── repositories 25 | ├── ... 26 | └── 27 | ├── channels 28 | │ ├── ... 29 | │ └── 30 | │ ├── ... 31 | │ └── 32 | ├── .meta 33 | ├── releases 34 | │ ├── ... 35 | │ └── 36 | │ └── _ 37 | │ └── ... 38 | └── scripts 39 | ├── ... 40 | └── - 41 | ├── ... 42 | └── source_script[.] 43 | ``` 44 | 45 | ```shell 46 | ~/.trdl$ cat config.yaml 47 | repositories: 48 | ... 49 | - name: 50 | url: 51 | defaultChannel: 52 | ``` 53 | -------------------------------------------------------------------------------- /client/cmd/trdl/bin_path.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | trdlClient "github.com/werf/trdl/client/pkg/client" 9 | "github.com/werf/trdl/client/pkg/trdl" 10 | ) 11 | 12 | func binPathCmd() *cobra.Command { 13 | cmd := &cobra.Command{ 14 | Use: "bin-path REPO GROUP [CHANNEL]", 15 | Short: "Get the directory with software binaries", 16 | DisableFlagsInUseLine: true, 17 | RunE: func(cmd *cobra.Command, args []string) error { 18 | if err := cobra.RangeArgs(2, 3)(cmd, args); err != nil { 19 | PrintHelp(cmd) 20 | return err 21 | } 22 | 23 | repoName := args[0] 24 | group := args[1] 25 | 26 | if repoName == trdl.SelfUpdateDefaultRepo { 27 | PrintHelp(cmd) 28 | return fmt.Errorf("reserved repository name %q cannot be used", trdl.SelfUpdateDefaultRepo) 29 | } 30 | 31 | var optionalChannel string 32 | if len(args) == 3 { 33 | optionalChannel = args[2] 34 | if err := ValidateChannel(optionalChannel); err != nil { 35 | PrintHelp(cmd) 36 | return err 37 | } 38 | } 39 | 40 | c, err := trdlClient.NewClient(homeDir) 41 | if err != nil { 42 | return fmt.Errorf("unable to initialize trdl client: %w", err) 43 | } 44 | 45 | dir, err := c.GetRepoChannelReleaseBinDir(repoName, group, optionalChannel) 46 | if err != nil { 47 | return err 48 | } 49 | 50 | fmt.Println(dir) 51 | 52 | return nil 53 | }, 54 | } 55 | 56 | return cmd 57 | } 58 | -------------------------------------------------------------------------------- /client/cmd/trdl/command/groups.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | ) 6 | 7 | type Group struct { 8 | Message string 9 | Commands []*cobra.Command 10 | } 11 | 12 | type Groups []Group 13 | 14 | func (g Groups) Add(c *cobra.Command) { 15 | for _, group := range g { 16 | c.AddCommand(group.Commands...) 17 | } 18 | } 19 | 20 | func (g Groups) Has(c *cobra.Command) bool { 21 | for _, group := range g { 22 | for _, command := range group.Commands { 23 | if command == c { 24 | return true 25 | } 26 | } 27 | } 28 | return false 29 | } 30 | -------------------------------------------------------------------------------- /client/cmd/trdl/common.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/asaskevich/govalidator" 8 | "github.com/spf13/cobra" 9 | 10 | "github.com/werf/common-go/pkg/util" 11 | "github.com/werf/trdl/client/pkg/trdl" 12 | ) 13 | 14 | func ValidateChannel(channel string) error { 15 | if !govalidator.IsIn(channel, trdl.Channels...) { 16 | return fmt.Errorf( 17 | "unable to parse argument \"CHANNEL\": unsupported channel %q specified, use one of the following: \"%s\"", 18 | channel, strings.Join(trdl.Channels, `", "`)) 19 | } 20 | 21 | return nil 22 | } 23 | 24 | func SetupNoSelfUpdate(cmd *cobra.Command, noSelfUpdate *bool) { 25 | envKey := "TRDL_NO_SELF_UPDATE" 26 | 27 | cmd.Flags().BoolVar(noSelfUpdate, 28 | "no-self-update", 29 | util.GetBoolEnvironmentDefaultFalse(envKey), 30 | fmt.Sprintf("Do not perform self-update (default $%s or false)", envKey)) 31 | } 32 | 33 | func PrintHelp(cmd *cobra.Command) { 34 | _ = cmd.Help() 35 | fmt.Println() 36 | } 37 | -------------------------------------------------------------------------------- /client/cmd/trdl/dir_path.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | trdlClient "github.com/werf/trdl/client/pkg/client" 9 | "github.com/werf/trdl/client/pkg/trdl" 10 | ) 11 | 12 | func dirPathCmd() *cobra.Command { 13 | cmd := &cobra.Command{ 14 | Use: "dir-path REPO GROUP [CHANNEL]", 15 | Short: "Get the directory with software artifacts", 16 | DisableFlagsInUseLine: true, 17 | RunE: func(cmd *cobra.Command, args []string) error { 18 | if err := cobra.RangeArgs(2, 3)(cmd, args); err != nil { 19 | PrintHelp(cmd) 20 | return err 21 | } 22 | 23 | repoName := args[0] 24 | group := args[1] 25 | 26 | if repoName == trdl.SelfUpdateDefaultRepo { 27 | PrintHelp(cmd) 28 | return fmt.Errorf("reserved repository name %q cannot be used", trdl.SelfUpdateDefaultRepo) 29 | } 30 | 31 | var optionalChannel string 32 | if len(args) == 3 { 33 | optionalChannel = args[2] 34 | if err := ValidateChannel(optionalChannel); err != nil { 35 | PrintHelp(cmd) 36 | return err 37 | } 38 | } 39 | 40 | c, err := trdlClient.NewClient(homeDir) 41 | if err != nil { 42 | return fmt.Errorf("unable to initialize trdl client: %w", err) 43 | } 44 | 45 | dir, err := c.GetRepoChannelReleaseDir(repoName, group, optionalChannel) 46 | if err != nil { 47 | return err 48 | } 49 | 50 | fmt.Println(dir) 51 | 52 | return nil 53 | }, 54 | } 55 | 56 | return cmd 57 | } 58 | -------------------------------------------------------------------------------- /client/cmd/trdl/list.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | 7 | "github.com/rodaine/table" 8 | "github.com/spf13/cobra" 9 | 10 | trdlClient "github.com/werf/trdl/client/pkg/client" 11 | "github.com/werf/trdl/client/pkg/trdl" 12 | ) 13 | 14 | func listCmd() *cobra.Command { 15 | cmd := &cobra.Command{ 16 | Use: "list", 17 | Short: "List registered repositories", 18 | DisableFlagsInUseLine: true, 19 | RunE: func(cmd *cobra.Command, args []string) error { 20 | c, err := trdlClient.NewClient(homeDir) 21 | if err != nil { 22 | return fmt.Errorf("unable to initialize trdl client: %w", err) 23 | } 24 | 25 | repoConfigurationList := c.GetRepoList() 26 | var repoNameList []string 27 | repoConfigurationByName := map[string]*trdlClient.RepoConfiguration{} 28 | for _, repoConfiguration := range repoConfigurationList { 29 | repoName := repoConfiguration.Name 30 | repoNameList = append(repoNameList, repoName) 31 | repoConfigurationByName[repoName] = repoConfiguration 32 | } 33 | 34 | sort.Strings(repoNameList) 35 | 36 | tbl := table.New("Name", "URL", "Default Channel") 37 | for _, repoName := range repoNameList { 38 | defaultChannel := repoConfigurationByName[repoName].DefaultChannel 39 | if defaultChannel == "" { 40 | defaultChannel = trdl.DefaultChannel 41 | } 42 | 43 | tbl.AddRow(repoName, repoConfigurationByName[repoName].Url, defaultChannel) 44 | } 45 | tbl.Print() 46 | 47 | return nil 48 | }, 49 | } 50 | 51 | return cmd 52 | } 53 | -------------------------------------------------------------------------------- /client/cmd/trdl/main_test.go: -------------------------------------------------------------------------------- 1 | //go:build test_coverage 2 | // +build test_coverage 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "os" 9 | "strings" 10 | "testing" 11 | 12 | "bou.ke/monkey" 13 | ) 14 | 15 | var exitCode int 16 | 17 | func TestMain(m *testing.M) { 18 | m.Run() 19 | os.Exit(exitCode) 20 | } 21 | 22 | func TestRunMain(t *testing.T) { 23 | // catch os.Exit 24 | fakeOsExit := func(code int) { 25 | exitCode = code 26 | panic(fmt.Sprintf("exit code %d", code)) 27 | } 28 | patch := monkey.Patch(os.Exit, fakeOsExit) 29 | defer patch.Unpatch() 30 | 31 | // catch and ignore fakeOsExit panic 32 | defer func() { 33 | if r := recover(); r != nil { 34 | if strings.HasPrefix(fmt.Sprint(r), "exit code") { 35 | return 36 | } 37 | 38 | panic(r) 39 | } 40 | }() 41 | 42 | // ignore test options 43 | oldArgs := os.Args 44 | var newArgs []string 45 | for _, arg := range os.Args { 46 | if strings.HasPrefix(arg, "-test.") { 47 | continue 48 | } 49 | newArgs = append(newArgs, arg) 50 | } 51 | os.Args = newArgs 52 | defer func() { 53 | os.Args = oldArgs 54 | }() 55 | 56 | // ignore test summary 57 | // PASS 58 | // coverage: 6.6% of statements in ./... 59 | defer discardStdOut() 60 | 61 | main() 62 | } 63 | 64 | func discardStdOut() { 65 | devnull, _ := os.OpenFile(os.DevNull, os.O_WRONLY, 0o755) 66 | os.Stdout = devnull 67 | } 68 | -------------------------------------------------------------------------------- /client/cmd/trdl/remove.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | trdlClient "github.com/werf/trdl/client/pkg/client" 9 | "github.com/werf/trdl/client/pkg/trdl" 10 | ) 11 | 12 | func removeCmd() *cobra.Command { 13 | cmd := &cobra.Command{ 14 | Use: "remove REPO", 15 | Short: "Remove a software repository", 16 | DisableFlagsInUseLine: true, 17 | RunE: func(cmd *cobra.Command, args []string) error { 18 | if err := cobra.ExactArgs(1)(cmd, args); err != nil { 19 | PrintHelp(cmd) 20 | return err 21 | } 22 | 23 | repoName := args[0] 24 | 25 | if repoName == trdl.SelfUpdateDefaultRepo { 26 | PrintHelp(cmd) 27 | return fmt.Errorf("reserved repository name %q cannot be used", trdl.SelfUpdateDefaultRepo) 28 | } 29 | 30 | c, err := trdlClient.NewClient(homeDir) 31 | if err != nil { 32 | return fmt.Errorf("unable to initialize trdl client: %w", err) 33 | } 34 | 35 | if err := c.RemoveRepo(repoName); err != nil { 36 | return err 37 | } 38 | 39 | return nil 40 | }, 41 | } 42 | 43 | return cmd 44 | } 45 | -------------------------------------------------------------------------------- /client/cmd/trdl/set_default_channel.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | trdlClient "github.com/werf/trdl/client/pkg/client" 9 | ) 10 | 11 | func setDefaultChannelCmd() *cobra.Command { 12 | cmd := &cobra.Command{ 13 | Use: "set-default-channel REPO CHANNEL", 14 | Short: "Set a default channel for a registered repository", 15 | Long: `Set a default channel for a registered repository. 16 | The new channel will be used by default instead of stable`, 17 | DisableFlagsInUseLine: true, 18 | RunE: func(cmd *cobra.Command, args []string) error { 19 | if err := cobra.ExactArgs(2)(cmd, args); err != nil { 20 | PrintHelp(cmd) 21 | return err 22 | } 23 | 24 | repoName := args[0] 25 | channel := args[1] 26 | 27 | if err := ValidateChannel(channel); err != nil { 28 | PrintHelp(cmd) 29 | return err 30 | } 31 | 32 | c, err := trdlClient.NewClient(homeDir) 33 | if err != nil { 34 | return fmt.Errorf("unable to initialize trdl client: %w", err) 35 | } 36 | 37 | if err := c.SetRepoDefaultChannel(repoName, channel); err != nil { 38 | return err 39 | } 40 | 41 | return nil 42 | }, 43 | } 44 | 45 | return cmd 46 | } 47 | -------------------------------------------------------------------------------- /client/cmd/trdl/version.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/werf/trdl/client/pkg/trdl" 9 | ) 10 | 11 | func versionCmd() *cobra.Command { 12 | return &cobra.Command{ 13 | Use: "version", 14 | Hidden: true, 15 | DisableFlagsInUseLine: true, 16 | Short: "Print version", 17 | Run: func(_ *cobra.Command, _ []string) { 18 | fmt.Println(trdl.Version) 19 | }, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /client/pkg/client/errors.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import "fmt" 4 | 5 | type RepositoryNotInitializedError struct { 6 | repoName string 7 | } 8 | 9 | func newRepositoryNotInitializedError(repoName string) error { 10 | return &RepositoryNotInitializedError{repoName: repoName} 11 | } 12 | 13 | func (e *RepositoryNotInitializedError) Error() string { 14 | return fmt.Sprintf( 15 | "repository %q not initialized: configure it with \"trdl add\" command", 16 | e.repoName, 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /client/pkg/repo/bin_path.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "github.com/werf/lockgate" 5 | "github.com/werf/trdl/client/pkg/trdl" 6 | ) 7 | 8 | func (c Client) GetChannelReleaseBinDir(group, channel string) (dir string, err error) { 9 | err = lockgate.WithAcquire(c.locker, c.channelLockName(group, channel), lockgate.AcquireOptions{Shared: true, Timeout: trdl.DefaultLockerTimeout}, func(_ bool) error { 10 | dir, _, err = c.findChannelReleaseBinDir(group, channel) 11 | return err 12 | }) 13 | 14 | return 15 | } 16 | 17 | func (c Client) GetChannelReleaseBinPath(group, channel, optionalBinName string) (path string, err error) { 18 | err = lockgate.WithAcquire(c.locker, c.channelLockName(group, channel), lockgate.AcquireOptions{Shared: true, Timeout: trdl.DefaultLockerTimeout}, func(_ bool) error { 19 | path, err = c.findChannelReleaseBinPath(group, channel, optionalBinName) 20 | return err 21 | }) 22 | 23 | return 24 | } 25 | -------------------------------------------------------------------------------- /client/pkg/repo/dir_path.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "github.com/werf/lockgate" 5 | "github.com/werf/trdl/client/pkg/trdl" 6 | ) 7 | 8 | func (c Client) GetChannelReleaseDir(group, channel string) (dir string, err error) { 9 | err = lockgate.WithAcquire(c.locker, c.channelLockName(group, channel), lockgate.AcquireOptions{Shared: true, Timeout: trdl.DefaultLockerTimeout}, func(_ bool) error { 10 | dir, _, err = c.findChannelReleaseDir(group, channel) 11 | return err 12 | }) 13 | 14 | return 15 | } 16 | -------------------------------------------------------------------------------- /client/pkg/repo/exec_path.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "github.com/werf/lockgate" 5 | "github.com/werf/trdl/client/pkg/trdl" 6 | "github.com/werf/trdl/client/pkg/util" 7 | ) 8 | 9 | func (c Client) ExecChannelReleaseBin(group, channel, optionalBinName string, args []string) error { 10 | return lockgate.WithAcquire(c.locker, c.channelLockName(group, channel), lockgate.AcquireOptions{Shared: true, Timeout: trdl.DefaultLockerTimeout}, func(_ bool) error { 11 | path, err := c.findChannelReleaseBinPath(group, channel, optionalBinName) 12 | if err != nil { 13 | return err 14 | } 15 | 16 | return util.Exec(path, args) 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /client/pkg/repo/interface.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/theupdateframework/go-tuf/data" 7 | ) 8 | 9 | type TufInterface interface { 10 | Setup(rootVersion int64, rootSha512 string) error 11 | Update() error 12 | DownloadFile(targetName, dest string, destMode os.FileMode) error 13 | GetTargets() (data.TargetFiles, error) 14 | } 15 | -------------------------------------------------------------------------------- /client/pkg/repo/setup.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | func (c Client) Setup(rootVersion int64, rootSha512 string) error { 4 | return c.tufClient.Setup(rootVersion, rootSha512) 5 | } 6 | -------------------------------------------------------------------------------- /client/pkg/trdl/const.go: -------------------------------------------------------------------------------- 1 | package trdl 2 | 3 | import "time" 4 | 5 | const ( 6 | ChannelDev = "dev" 7 | ChannelAlpha = "alpha" 8 | ChannelBeta = "beta" 9 | ChannelEA = "ea" 10 | ChannelStable = "stable" 11 | ChannelRockSolid = "rock-solid" 12 | DefaultChannel = ChannelStable 13 | 14 | ShellUnix = "unix" 15 | ShellPowerShell = "pwsh" 16 | 17 | SelfUpdateDefaultRepo = "trdl" 18 | SelfUpdateDefaultUrl = "https://tuf.trdl.dev" 19 | SelfUpdateDefaultRootVersion = 1 20 | SelfUpdateDefaultRootSha512 = "14e4127ef0fa3e34a6524eb6b540ff478766c5e5254b3687bbe8e727da2e748377f02f5c68d41c876990c7b6884656b55dd9992a555a35a76a6e2cdd23564501" 21 | SelfUpdateDefaultGroup = "0" 22 | ) 23 | 24 | var Channels = []string{ 25 | ChannelDev, 26 | ChannelAlpha, 27 | ChannelBeta, 28 | ChannelEA, 29 | ChannelStable, 30 | ChannelRockSolid, 31 | } 32 | 33 | var DefaultLockerTimeout = 30 * time.Second 34 | -------------------------------------------------------------------------------- /client/pkg/trdl/version.go: -------------------------------------------------------------------------------- 1 | package trdl 2 | 3 | var Version = "dev" 4 | -------------------------------------------------------------------------------- /client/pkg/tuf/download.go: -------------------------------------------------------------------------------- 1 | package tuf 2 | 3 | import ( 4 | "io" 5 | "os" 6 | "path/filepath" 7 | 8 | tufClient "github.com/theupdateframework/go-tuf/client" 9 | tufUtil "github.com/theupdateframework/go-tuf/util" 10 | ) 11 | 12 | func (c Client) DownloadFile(targetName, dest string, destMode os.FileMode) error { 13 | if err := os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil { 14 | return err 15 | } 16 | 17 | f, err := os.OpenFile(dest, os.O_RDWR|os.O_CREATE, destMode) 18 | if err != nil { 19 | return err 20 | } 21 | defer func() { _ = f.Close() }() 22 | 23 | file := destinationFile{f} 24 | if err := c.Download(targetName, &file); err != nil { 25 | return err 26 | } 27 | 28 | return nil 29 | } 30 | 31 | type destinationFile struct { 32 | *os.File 33 | } 34 | 35 | func (t *destinationFile) Delete() error { 36 | _ = t.Close() 37 | return os.Remove(t.Name()) 38 | } 39 | 40 | func (c Client) Download(targetName string, destination tufClient.Destination) error { 41 | return c.Client.Download(tufUtil.NormalizeTarget(targetName), destination) 42 | } 43 | 44 | func (c Client) DownloadMeta(name string) ([]byte, error) { 45 | ioReader, _, err := c.RemoteStore.GetMeta(name) 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | return io.ReadAll(ioReader) 51 | } 52 | -------------------------------------------------------------------------------- /client/pkg/tuf/trace.go: -------------------------------------------------------------------------------- 1 | package tuf 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptrace" 6 | "time" 7 | 8 | "github.com/werf/trdl/client/pkg/logger" 9 | ) 10 | 11 | type TracingTransport struct { 12 | Transport http.RoundTripper 13 | Logger logger.Logger 14 | } 15 | 16 | func (t *TracingTransport) RoundTrip(req *http.Request) (*http.Response, error) { 17 | log := t.Logger.With("source", "tuf-client") 18 | startTime := time.Now() 19 | 20 | log.Debug("Request started", 21 | "method", req.Method, 22 | "url", req.URL.String(), 23 | ) 24 | 25 | trace := &httptrace.ClientTrace{ 26 | DNSDone: func(info httptrace.DNSDoneInfo) { 27 | log.Debug("DNS lookup done", "host", info.Addrs) 28 | }, 29 | ConnectDone: func(network, addr string, err error) { 30 | if err != nil { 31 | log.Debug("Failed to connect", 32 | "host", addr, 33 | "error", err, 34 | ) 35 | return 36 | } 37 | log.Debug("Connected", "address", addr) 38 | }, 39 | } 40 | 41 | req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace)) 42 | 43 | resp, err := t.Transport.RoundTrip(req) 44 | if err != nil { 45 | log.Debug("Failed to send request", 46 | "url", req.URL.String(), 47 | "error", err, 48 | ) 49 | return nil, err 50 | } 51 | 52 | log.Debug("Request completed", 53 | "status", resp.Status, 54 | "duration", time.Since(startTime).Seconds(), 55 | ) 56 | 57 | return resp, nil 58 | } 59 | -------------------------------------------------------------------------------- /client/pkg/util/exec_unix.go: -------------------------------------------------------------------------------- 1 | //go:build darwin || linux 2 | // +build darwin linux 3 | 4 | package util 5 | 6 | import ( 7 | "fmt" 8 | "os" 9 | "syscall" 10 | ) 11 | 12 | func Exec(path string, args []string) error { 13 | args = append([]string{path}, args...) 14 | err := syscall.Exec(path, args, os.Environ()) 15 | if err != nil { 16 | return fmt.Errorf("unable to exec path %q: %w", path, err) 17 | } 18 | 19 | return nil 20 | } 21 | -------------------------------------------------------------------------------- /client/pkg/util/exec_windows.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/exec" 7 | ) 8 | 9 | func Exec(path string, args []string) error { 10 | cmd := exec.Command(path, args...) 11 | cmd.Stdout = os.Stdout 12 | cmd.Stderr = os.Stderr 13 | cmd.Stdin = os.Stdin 14 | 15 | if err := cmd.Run(); err != nil { 16 | return fmt.Errorf("unable to run command: %q", err) 17 | } 18 | 19 | os.Exit(0) 20 | 21 | return nil 22 | } 23 | -------------------------------------------------------------------------------- /client/pkg/util/file.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | ) 7 | 8 | func IsDirExist(path string) (bool, error) { 9 | fileInfo, err := os.Lstat(path) 10 | if err != nil { 11 | if isNotExistErr(err) { 12 | return false, nil 13 | } 14 | 15 | return false, err 16 | } 17 | 18 | return fileInfo.IsDir(), nil 19 | } 20 | 21 | func IsRegularFileExist(path string) (bool, error) { 22 | fileInfo, err := os.Lstat(path) 23 | if err != nil { 24 | if isNotExistErr(err) { 25 | return false, nil 26 | } 27 | 28 | return false, err 29 | } 30 | 31 | return fileInfo.Mode().IsRegular(), nil 32 | } 33 | 34 | func isNotExistErr(err error) bool { 35 | return os.IsNotExist(err) || IsNotDirectoryErr(err) 36 | } 37 | 38 | func IsNotDirectoryErr(err error) bool { 39 | return strings.HasSuffix(err.Error(), "not a directory") 40 | } 41 | -------------------------------------------------------------------------------- /client/pkg/util/hashsum.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "crypto/sha512" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/spaolacci/murmur3" 9 | ) 10 | 11 | func Sha512Checksum(data []byte) string { 12 | h := sha512.New() 13 | h.Write(data) 14 | return fmt.Sprintf("%x", h.Sum(nil)) 15 | } 16 | 17 | func MurmurHash(args ...string) string { 18 | h32 := murmur3.New32() 19 | _, _ = h32.Write([]byte(strings.Join(args, ":::"))) 20 | sum := h32.Sum32() 21 | return fmt.Sprintf("%x", sum) 22 | } 23 | -------------------------------------------------------------------------------- /client/pkg/util/path.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "os/user" 5 | "path/filepath" 6 | "strings" 7 | ) 8 | 9 | func ExpandPath(path string) (string, error) { 10 | var result string 11 | if strings.HasPrefix(path, "~") { 12 | usr, err := user.Current() 13 | if err != nil { 14 | return "", err 15 | } 16 | 17 | dir := usr.HomeDir 18 | if path == "~" { 19 | result = dir 20 | } else { 21 | result = filepath.Join(dir, path[2:]) 22 | } 23 | } else { 24 | var err error 25 | result, err = filepath.Abs(path) 26 | if err != nil { 27 | return "", err 28 | } 29 | } 30 | 31 | return result, nil 32 | } 33 | -------------------------------------------------------------------------------- /client/scripts/verify-dist-binaries.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | script_dir="$(cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" 5 | project_dir="$script_dir/.." 6 | 7 | version="${1:?Version should be set}" 8 | 9 | declare -A regexps 10 | regexps["$project_dir/dist/$version/linux-amd64/bin/trdl"]="x86-64.*statically linked" 11 | regexps["$project_dir/dist/$version/linux-arm64/bin/trdl"]="ARM aarch64.*statically linked" 12 | regexps["$project_dir/dist/$version/darwin-amd64/bin/trdl"]="Mach-O.*x86_64" 13 | regexps["$project_dir/dist/$version/darwin-arm64/bin/trdl"]="Mach-O.*arm64" 14 | regexps["$project_dir/dist/$version/windows-amd64/bin/trdl.exe"]="x86-64.*Windows" 15 | 16 | for filename in "${!regexps[@]}"; do 17 | if ! [[ -f "$filename" ]]; then 18 | echo Binary at "$filename" does not exist. 19 | exit 1 20 | fi 21 | 22 | file "$filename" | awk -v regexp="${regexps[$filename]}" '{print $0; if ($0 ~ regexp) { exit } else { print "Unexpected binary info ^^"; exit 1 }}' 23 | done -------------------------------------------------------------------------------- /client/trdl.yaml: -------------------------------------------------------------------------------- 1 | docker_image: registry.werf.io/trdl/builder:51b030fe472ec6caa59b068a364bb835ea140588@sha256:f71af3da98446de12e5bb47a789517ba1a17cb19fb17cfc78699552ebcc2c3cf 2 | commands: 3 | - task client:build:dist version={{ .Tag }} 4 | - task client:verify:dist:binaries version={{ .Tag }} 5 | - cp -a client/dist/{{ .Tag }}/* /result 6 | -------------------------------------------------------------------------------- /client/trdl_channels.yaml: -------------------------------------------------------------------------------- 1 | groups: 2 | - name: "0" 3 | channels: 4 | - name: alpha 5 | version: 0.11.0 6 | - name: stable 7 | version: 0.11.0 8 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _site/ 2 | .sass-cache/ 3 | .jekyll-metadata/ 4 | .jekyll-cache/ 5 | .jekyll-bundle/ 6 | docs/ 7 | .prettierignore -------------------------------------------------------------------------------- /docs/.helm/templates/20-ingress-tuf-router.yaml: -------------------------------------------------------------------------------- 1 | {{- $host := pluck .Values.werf.env .Values.host | first | default .Values.host._default }} 2 | {{- if hasPrefix "review" .Values.werf.env }} 3 | {{- $host = ( printf "%s.%s" .Values.werf.env (pluck "dev" .Values.host | first | default .Values.host._default ) | lower ) }} 4 | {{- end }} 5 | 6 | apiVersion: networking.k8s.io/v1 7 | kind: Ingress 8 | metadata: 9 | name: tuf-router 10 | annotations: 11 | spec: 12 | ingressClassName: nginx 13 | tls: 14 | - hosts: 15 | - {{ $host }} 16 | - ru.{{ $host }} 17 | secretName: tls-{{ $host }} 18 | rules: 19 | - host: {{ $host }} 20 | http: 21 | paths: 22 | - path: /targets 23 | pathType: Prefix 24 | backend: 25 | service: 26 | name: tuf-router 27 | port: 28 | name: http 29 | - path: /download 30 | pathType: Prefix 31 | backend: 32 | service: 33 | name: tuf-router 34 | port: 35 | name: http 36 | - host: ru.{{ $host }} 37 | http: 38 | paths: 39 | - path: /targets 40 | pathType: Prefix 41 | backend: 42 | service: 43 | name: tuf-router 44 | port: 45 | name: http 46 | - path: /download 47 | pathType: Prefix 48 | backend: 49 | service: 50 | name: tuf-router 51 | port: 52 | name: http 53 | -------------------------------------------------------------------------------- /docs/.helm/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{- define "resources" }} 2 | resources: 3 | requests: 4 | memory: {{ pluck .Values.werf.env .Values.resources.requests.memory | first | default .Values.resources.requests.memory._default }} 5 | limits: 6 | memory: {{ pluck .Values.werf.env .Values.resources.requests.memory | first | default .Values.resources.requests.memory._default }} 7 | {{- end }} -------------------------------------------------------------------------------- /docs/.helm/templates/_rewrites.tpl: -------------------------------------------------------------------------------- 1 | {{- define "rewrites" }} 2 | {{- end }} 3 | -------------------------------------------------------------------------------- /docs/.helm/values.yaml: -------------------------------------------------------------------------------- 1 | priorityClassName: 2 | _default: develop 3 | production: production-medium 4 | 5 | host: 6 | production: trdl.dev 7 | _default: trdl.test.flant.com 8 | 9 | resources: 10 | requests: 11 | memory: 12 | _default: 20M 13 | production: 50M 14 | -------------------------------------------------------------------------------- /docs/.werf/tuf-router.lua: -------------------------------------------------------------------------------- 1 | local dst_url 2 | local res 3 | 4 | ngx.var.arch = string.lower(ngx.var.arch) 5 | ngx.var.arch = string.gsub(ngx.var.arch, 'x86_64', 'amd64') 6 | ngx.var.arch = string.gsub(ngx.var.arch, 'aarch64', 'arm64') 7 | local m, err = ngx.re.match(ngx.var.target, '.sig$') 8 | 9 | if m then 10 | dst_url = '/targets/signature/releases/' 11 | else 12 | dst_url = '/targets/releases/' 13 | end 14 | 15 | local version_request_url = string.format('/targets/channels/%s/%s', ngx.var.group, ngx.var.channel) 16 | 17 | -- A chain of redirects is not working well, so, avoid it here 18 | local max_hop = 1 19 | repeat 20 | res = ngx.location.capture(version_request_url) 21 | if res.status == ngx.HTTP_MOVED_PERMANENTLY or res.status == ngx.HTTP_MOVED_TEMPORARILY then 22 | ngx.log(ngx.NOTICE, string.format('Got redirect %s to %s (requested - %s)', res.status, res.header['Location'], version_request_url)) 23 | version_request_url = res.header['Location'] 24 | end 25 | max_hop = max_hop - 1 26 | until not ( max_hop > 0 and res.status ~= ngx.HTTP_OK ) 27 | 28 | if res.status == ngx.HTTP_OK then 29 | -- Is this enough to validate the version? 30 | ngx.var.version = string.gsub(res.body,'[^a-zA-Z0-9.+-]','') 31 | ngx.header["Cache-Control"] = 'no-store, no-cache' 32 | return ngx.redirect(string.format('%s%s%s/%s-%s/bin/%s', ngx.var.tuf_repo_url, dst_url, ngx.var.version, ngx.var.os, ngx.var.arch, ngx.var.target), 302) 33 | else 34 | ngx.log(ngx.WARN, string.format('Got status %s when trying to request version (URL - %s)', res.status, version_request_url)) 35 | ngx.status = res.status 36 | return ngx.exit(res.status) 37 | end 38 | -------------------------------------------------------------------------------- /docs/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "jekyll-assets" 4 | gem "jekyll" 5 | gem "kramdown-parser-gfm" 6 | 7 | gem "sprockets", "~> 3.7" 8 | gem "nokogiri", ">= 1.10.8" 9 | gem "kramdown", ">= 2.3.0" 10 | 11 | gem "html-proofer" 12 | -------------------------------------------------------------------------------- /docs/LOCALDEV.md: -------------------------------------------------------------------------------- 1 | # How to start documentation container locally? 2 | 3 | Don't forget to clone repo firstly. 4 | 5 | 1. Free 80 port to bind 6 | 7 | Stop other service or make appropriate changes to the commands below. 8 | 9 | 2. Open separate console and start container 10 | ```shell 11 | cd docs 12 | source $(trdl use werf 1.2 ea) 13 | werf run --follow --docker-options="-d --name trdl -p 80:80" --dev web 14 | ``` 15 | 16 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | topnav_title: trdl 2 | site_title: trdl 3 | company_name: Flant 4 | company_url: https://flant.com 5 | site_lang: en 6 | site_urls: 7 | ru: ru.trdl.dev 8 | en: trdl.dev 9 | site_description: Deliver your software continuously & securely. 10 | github_repo_path: /werf/trdl 11 | 12 | exclude: 13 | - .gitignore 14 | - Gemfile 15 | - Gemfile.lock 16 | - werf.yaml 17 | - pages_ru/ 18 | 19 | highlighter: rouge 20 | 21 | plugins: 22 | - jekyll-assets 23 | 24 | markdown: kramdown 25 | kramdown: 26 | input: GFM 27 | hard_wrap: false 28 | syntax_highlighter: rouge 29 | 30 | breadcrumbs: 31 | root: 32 | hide: false # show breadcrumbs on root/home page 33 | image: false # Show image or title text 34 | hometext: "Home" 35 | 36 | collections: 37 | tooltips: 38 | output: false 39 | 40 | defaults: 41 | - scope: 42 | path: "" 43 | type: "pages" 44 | values: 45 | layout: "page" 46 | search: true 47 | sidebar: false 48 | topnav: topnav 49 | sitemap_include: true 50 | multilang: true 51 | - scope: 52 | path: "pages_*/reference" 53 | type: "pages" 54 | values: 55 | sidebar: reference 56 | - scope: 57 | path: "pages_*/reference/cli" 58 | type: "pages" 59 | values: 60 | toc: false 61 | - scope: 62 | path: "pages_*/reference/vault_plugin/*" 63 | type: "pages" 64 | values: 65 | toc: true 66 | - scope: 67 | path: "pages_*/reference/vault_plugin/index.md" 68 | type: "pages" 69 | values: 70 | toc: false 71 | 72 | timezone: Etc/GMT-3 73 | output: web 74 | -------------------------------------------------------------------------------- /docs/_config_ru.yml: -------------------------------------------------------------------------------- 1 | company_name: Флант 2 | company_url: https://flant.ru 3 | site_lang: ru 4 | site_description: Инструмент безопасной доставки вашего кода пользователям. 5 | 6 | exclude: 7 | - .gitignore 8 | - Gemfile 9 | - Gemfile.lock 10 | - werf.yaml 11 | - pages_en/ 12 | 13 | include: 14 | - pages_ru/ 15 | 16 | breadcrumbs: 17 | hometext: "Начало" 18 | -------------------------------------------------------------------------------- /docs/_data/sidebars/_cli.yml: -------------------------------------------------------------------------------- 1 | # This file is generated by "trdl docs" command. 2 | # DO NOT EDIT! 3 | 4 | cli: &cli 5 | 6 | - title: Overview 7 | url: /reference/cli/overview.html 8 | 9 | - title: Configuration commands 10 | f: 11 | 12 | - title: trdl add 13 | url: /reference/cli/trdl_add.html 14 | 15 | - title: trdl remove 16 | url: /reference/cli/trdl_remove.html 17 | 18 | - title: trdl list 19 | url: /reference/cli/trdl_list.html 20 | 21 | - title: trdl set-default-channel 22 | url: /reference/cli/trdl_set_default_channel.html 23 | 24 | - title: Main commands 25 | f: 26 | 27 | - title: trdl use 28 | url: /reference/cli/trdl_use.html 29 | 30 | - title: Advanced commands 31 | f: 32 | 33 | - title: trdl update 34 | url: /reference/cli/trdl_update.html 35 | 36 | - title: trdl exec 37 | url: /reference/cli/trdl_exec.html 38 | 39 | - title: trdl dir-path 40 | url: /reference/cli/trdl_dir_path.html 41 | 42 | - title: trdl bin-path 43 | url: /reference/cli/trdl_bin_path.html 44 | -------------------------------------------------------------------------------- /docs/_data/sidebars/_reference.yml: -------------------------------------------------------------------------------- 1 | entries: 2 | en: 3 | f: 4 | - title: Command line interface 5 | f: *cli 6 | 7 | - title: Vault plugin 8 | f: *_vault_plugin 9 | 10 | - &trdl_yaml 11 | title: trdl.yaml 12 | url: /reference/trdl_yaml.html 13 | 14 | - &trdl_channels_yaml 15 | title: trdl_channels.yaml 16 | url: /reference/trdl_channels_yaml.html 17 | 18 | - &tuf_repository_layout 19 | title: TUF repository layout 20 | url: /reference/tuf_repository_layout.html 21 | 22 | ru: 23 | f: 24 | - title: CLI 25 | f: *cli 26 | 27 | - title: Плагин vault 28 | f: *_vault_plugin 29 | 30 | - *trdl_yaml 31 | 32 | - *trdl_channels_yaml 33 | 34 | - <<: *tuf_repository_layout 35 | title: "Организация TUF-репозитория" 36 | -------------------------------------------------------------------------------- /docs/_data/topnav.yml: -------------------------------------------------------------------------------- 1 | topnav: 2 | en: 3 | - title: Security 4 | url: /security.html 5 | 6 | - title: Quickstart 7 | url: /quickstart.html 8 | 9 | - title: Reference 10 | url: /reference/cli/overview.html 11 | 12 | - title: GitHub 13 | external_url: https://github.com/werf/trdl 14 | 15 | ru: 16 | - title: Безопасность 17 | url: /security.html 18 | 19 | - title: Быстрый старт 20 | url: /quickstart.html 21 | 22 | - title: Справочник 23 | url: /reference/cli/overview.html 24 | 25 | - title: GitHub 26 | external_url: https://github.com/werf/trdl 27 | -------------------------------------------------------------------------------- /docs/_data/trdl.yml: -------------------------------------------------------------------------------- 1 | directives: 2 | - name: dockerImage 3 | value: "string" 4 | required: true 5 | description: 6 | en: Docker image name. Repository and digest are mandatory `REPO[:TAG]@DIGEST` (e.g. `ubuntu:18.04@sha256:538529c9d229fb55f50e6746b119e899775205d62c0fc1b7e679b30d02ecb6e8`) 7 | ru: Имя docker образа. Репозиторий и digest обязательны `REPO[:TAG]@DIGEST` (к примеру, `ubuntu:18.04@sha256:538529c9d229fb55f50e6746b119e899775205d62c0fc1b7e679b30d02ecb6e8`) 8 | - name: commands 9 | value: "[ string, ... ]" 10 | required: true 11 | description: 12 | en: Build instructions. The instructions can use the `{{ .Tag }}` pattern, which is replaced by a git tag 13 | ru: Сборочные инструкции. В инструкциях можно использовать шаблон `{{ .Tag }}`, который заменяется на собираемый git-tag 14 | -------------------------------------------------------------------------------- /docs/_data/trdl_channels.yml: -------------------------------------------------------------------------------- 1 | directives: 2 | - name: groups 3 | description: 4 | en: Groups 5 | ru: Группы 6 | directiveList: 7 | - name: name 8 | value: string 9 | description: 10 | en: "Group name, semver arbitrary part (`MAJOR.MINOR.PATCH`). E.g. `1`, `1.2` or `1.2.3`" 11 | ru: "Имя группы, произвольная часть semver (`MAJOR.MINOR.PATCH`). К примеру: `1`, `1.2` или `1.2.3`" 12 | - name: channels 13 | description: 14 | en: Group release channels 15 | ru: Каналы обновлений группы 16 | directiveList: 17 | - name: name 18 | value: "string" 19 | required: true 20 | description: 21 | en: "Release channel name: alpha, beta, ea, stable or rock-solid" 22 | ru: "Имя канала обновлений: alpha, beta, ea, stable или rock-solid" 23 | - name: version 24 | value: "string" 25 | required: true 26 | description: 27 | en: Existing version 28 | ru: Существующая версия 29 | -------------------------------------------------------------------------------- /docs/_includes/index_en/how-work-trdl.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
How does trdl work?
5 |
Let's look at how the project team uses trdl to continuously deliver updates to users. The process in its simplified form involves three main steps: pushing the release to the TUF repository, publishing the release channel, and delivering the release to the user via the published release channel.
6 | 7 |
8 |
9 |
-------------------------------------------------------------------------------- /docs/_includes/index_en/solve-that.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | Sound familiar?
6 | Consider trdl to solve your problems! 7 |
8 |
9 | 10 | 11 | 12 | 15 |
16 |
17 |
18 |
-------------------------------------------------------------------------------- /docs/_includes/index_ru/how-work-trdl.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
Как работает trdl
5 | 6 |
7 |
8 |
9 | -------------------------------------------------------------------------------- /docs/_includes/index_ru/solve-that.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | Знакомые проблемы?
6 | Решите их с помощью trdl! 7 |
8 |
9 | 10 | 11 | 12 | 15 |
16 |
17 |
18 |
-------------------------------------------------------------------------------- /docs/_includes/menu-burger.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
-------------------------------------------------------------------------------- /docs/_includes/nav-breadcrumbs.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl.md: -------------------------------------------------------------------------------- 1 | The universal package manager for delivering your software updates securely from a TUF repository (more details on [https://trdl.dev](https://trdl.dev)) 2 | 3 | ## Options 4 | 5 | ```shell 6 | -d, --debug=false 7 | Enable debug output (default $TRDL_DEBUG or false) 8 | --home-dir='~/.trdl' 9 | Set trdl home directory (default $TRDL_HOME_DIR or ~/.trdl) 10 | ``` 11 | 12 | -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl.short.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/_includes/reference/cli/trdl.short.md -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl_.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Options inherited from parent commands 4 | 5 | ```shell 6 | -d, --debug=false 7 | Enable debug output (default $TRDL_DEBUG or false) 8 | --home-dir='~/.trdl' 9 | Set trdl home directory (default $TRDL_HOME_DIR or ~/.trdl) 10 | ``` 11 | 12 | -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl_.short.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/_includes/reference/cli/trdl_.short.md -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl_add.md: -------------------------------------------------------------------------------- 1 | Add a software repository 2 | 3 | ## Syntax 4 | 5 | ```shell 6 | trdl add REPO URL [ROOT_VERSION] [ROOT_SHA512] 7 | ``` 8 | 9 | ## Options inherited from parent commands 10 | 11 | ```shell 12 | -d, --debug=false 13 | Enable debug output (default $TRDL_DEBUG or false) 14 | --home-dir='~/.trdl' 15 | Set trdl home directory (default $TRDL_HOME_DIR or ~/.trdl) 16 | ``` 17 | 18 | -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl_add.short.md: -------------------------------------------------------------------------------- 1 | add a software repository -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl_bin_path.md: -------------------------------------------------------------------------------- 1 | Get the directory with software binaries 2 | 3 | ## Syntax 4 | 5 | ```shell 6 | trdl bin-path REPO GROUP [CHANNEL] 7 | ``` 8 | 9 | ## Options inherited from parent commands 10 | 11 | ```shell 12 | -d, --debug=false 13 | Enable debug output (default $TRDL_DEBUG or false) 14 | --home-dir='~/.trdl' 15 | Set trdl home directory (default $TRDL_HOME_DIR or ~/.trdl) 16 | ``` 17 | 18 | -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl_bin_path.short.md: -------------------------------------------------------------------------------- 1 | get the directory with software binaries -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl_dir_path.md: -------------------------------------------------------------------------------- 1 | Get the directory with software artifacts 2 | 3 | ## Syntax 4 | 5 | ```shell 6 | trdl dir-path REPO GROUP [CHANNEL] 7 | ``` 8 | 9 | ## Options inherited from parent commands 10 | 11 | ```shell 12 | -d, --debug=false 13 | Enable debug output (default $TRDL_DEBUG or false) 14 | --home-dir='~/.trdl' 15 | Set trdl home directory (default $TRDL_HOME_DIR or ~/.trdl) 16 | ``` 17 | 18 | -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl_dir_path.short.md: -------------------------------------------------------------------------------- 1 | get the directory with software artifacts -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl_docs.md: -------------------------------------------------------------------------------- 1 | Generate documentation as markdown 2 | 3 | ## Syntax 4 | 5 | ```shell 6 | trdl docs JEKYLL_SITE_DIR [options] 7 | ``` 8 | 9 | ## Options 10 | 11 | ```shell 12 | -h, --help=false 13 | help for docs 14 | ``` 15 | 16 | ## Options inherited from parent commands 17 | 18 | ```shell 19 | -d, --debug=false 20 | Enable debug output (default $TRDL_DEBUG or false) 21 | --home-dir='~/.trdl' 22 | Set trdl home directory (default $TRDL_HOME_DIR or ~/.trdl) 23 | ``` 24 | 25 | -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl_docs.short.md: -------------------------------------------------------------------------------- 1 | generate documentation as markdown -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl_exec.md: -------------------------------------------------------------------------------- 1 | Exec a software binary 2 | 3 | ## Syntax 4 | 5 | ```shell 6 | trdl exec REPO GROUP [CHANNEL] [BINARY_NAME] [--] [ARGS] 7 | ``` 8 | 9 | ## Options inherited from parent commands 10 | 11 | ```shell 12 | -d, --debug=false 13 | Enable debug output (default $TRDL_DEBUG or false) 14 | --home-dir='~/.trdl' 15 | Set trdl home directory (default $TRDL_HOME_DIR or ~/.trdl) 16 | ``` 17 | 18 | -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl_exec.short.md: -------------------------------------------------------------------------------- 1 | exec a software binary -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl_list.md: -------------------------------------------------------------------------------- 1 | List registered repositories 2 | 3 | ## Syntax 4 | 5 | ```shell 6 | trdl list 7 | ``` 8 | 9 | ## Options inherited from parent commands 10 | 11 | ```shell 12 | -d, --debug=false 13 | Enable debug output (default $TRDL_DEBUG or false) 14 | --home-dir='~/.trdl' 15 | Set trdl home directory (default $TRDL_HOME_DIR or ~/.trdl) 16 | ``` 17 | 18 | -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl_list.short.md: -------------------------------------------------------------------------------- 1 | list registered repositories -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl_remove.md: -------------------------------------------------------------------------------- 1 | Remove a software repository 2 | 3 | ## Syntax 4 | 5 | ```shell 6 | trdl remove REPO 7 | ``` 8 | 9 | ## Options inherited from parent commands 10 | 11 | ```shell 12 | -d, --debug=false 13 | Enable debug output (default $TRDL_DEBUG or false) 14 | --home-dir='~/.trdl' 15 | Set trdl home directory (default $TRDL_HOME_DIR or ~/.trdl) 16 | ``` 17 | 18 | -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl_remove.short.md: -------------------------------------------------------------------------------- 1 | remove a software repository -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl_set_default_channel.md: -------------------------------------------------------------------------------- 1 | Set a default channel for a registered repository. 2 | The new channel will be used by default instead of stable 3 | 4 | ## Syntax 5 | 6 | ```shell 7 | trdl set-default-channel REPO CHANNEL 8 | ``` 9 | 10 | ## Options inherited from parent commands 11 | 12 | ```shell 13 | -d, --debug=false 14 | Enable debug output (default $TRDL_DEBUG or false) 15 | --home-dir='~/.trdl' 16 | Set trdl home directory (default $TRDL_HOME_DIR or ~/.trdl) 17 | ``` 18 | 19 | -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl_set_default_channel.short.md: -------------------------------------------------------------------------------- 1 | set a default channel for a registered repository -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl_update.md: -------------------------------------------------------------------------------- 1 | Update the software 2 | 3 | ## Syntax 4 | 5 | ```shell 6 | trdl update REPO GROUP [CHANNEL] [options] 7 | ``` 8 | 9 | ## Options 10 | 11 | ```shell 12 | --autoclean=true 13 | Erase old downloaded releases (default $TRDL_AUTOCLEAN or true) 14 | --background-stderr-file='' 15 | Redirect the stderr of the background update to a file (default $TRDL_BACKGROUND_STDERR_FILE or none) 16 | --background-stdout-file='' 17 | Redirect the stdout of the background update to a file (default $TRDL_BACKGROUND_STDOUT_FILE or none) 18 | --in-background=false 19 | Perform update in background (default $TRDL_IN_BACKGROUND or false) 20 | --no-self-update=false 21 | Do not perform self-update (default $TRDL_NO_SELF_UPDATE or false) 22 | ``` 23 | 24 | ## Options inherited from parent commands 25 | 26 | ```shell 27 | -d, --debug=false 28 | Enable debug output (default $TRDL_DEBUG or false) 29 | --home-dir='~/.trdl' 30 | Set trdl home directory (default $TRDL_HOME_DIR or ~/.trdl) 31 | ``` 32 | 33 | -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl_update.short.md: -------------------------------------------------------------------------------- 1 | update the software -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl_use.md: -------------------------------------------------------------------------------- 1 | Generate a script to update the software binaries in the background and use local ones within a shell session 2 | 3 | ## Syntax 4 | 5 | ```shell 6 | trdl use REPO GROUP [CHANNEL] [options] 7 | ``` 8 | 9 | ## Examples 10 | 11 | ```shell 12 | # Source script in a shell 13 | $ . $(trdl use repo_name 1.2 ea) 14 | 15 | # Force script generation for a Unix shell on Windows 16 | $ trdl use repo_name 1.2 ea --shell unix 17 | 18 | ``` 19 | 20 | ## Options 21 | 22 | ```shell 23 | --no-self-update=false 24 | Do not perform self-update (default $TRDL_NO_SELF_UPDATE or false) 25 | --shell='unix' 26 | Select the shell for which to prepare the script. 27 | Supports `pwsh` and `unix` shells (default $TRDL_SHELL, `pwsh` for Windows or `unix`) 28 | ``` 29 | 30 | ## Options inherited from parent commands 31 | 32 | ```shell 33 | -d, --debug=false 34 | Enable debug output (default $TRDL_DEBUG or false) 35 | --home-dir='~/.trdl' 36 | Set trdl home directory (default $TRDL_HOME_DIR or ~/.trdl) 37 | ``` 38 | 39 | -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl_use.short.md: -------------------------------------------------------------------------------- 1 | generate a script to use the software binaries within a shell session -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl_version.md: -------------------------------------------------------------------------------- 1 | Print version 2 | 3 | ## Syntax 4 | 5 | ```shell 6 | trdl version 7 | ``` 8 | 9 | ## Options inherited from parent commands 10 | 11 | ```shell 12 | -d, --debug=false 13 | Enable debug output (default $TRDL_DEBUG or false) 14 | --home-dir='~/.trdl' 15 | Set trdl home directory (default $TRDL_HOME_DIR or ~/.trdl) 16 | ``` 17 | 18 | -------------------------------------------------------------------------------- /docs/_includes/reference/cli/trdl_version.short.md: -------------------------------------------------------------------------------- 1 | print version -------------------------------------------------------------------------------- /docs/_includes/reference/trdl_channels_yaml/table.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | {{ "tableLineCounter" | reset_shared_counter }} 6 | {% for directive in site.data.trdl_channels.directives %} 7 | {% include reference/configuration_table_directive.html directive=directive %} 8 | {% endfor %} 9 | 10 |
-------------------------------------------------------------------------------- /docs/_includes/reference/trdl_yaml/example_build_sh.md.liquid: -------------------------------------------------------------------------------- 1 | {% raw %} 2 | ```shell 3 | #!/bin/sh -e 4 | VERSION=$1 5 | if [ -z "$VERSION" ] ; then 6 | echo "Required version argument!" 1>&2 7 | echo 1>&2 8 | echo "Usage: $0 VERSION" 1>&2 9 | exit 1 10 | fi 11 | mkdir -p release-build/${VERSION}/any-any/bin 12 | printf "echo ${VERSION}\n" > release-build/${VERSION}/any-any/bin/trdl-example.sh 13 | mkdir -p release-build/${VERSION}/windows-any/bin 14 | printf "@echo off\necho ${VERSION}\n" > release-build/${VERSION}/windows-any/bin/trdl-example.ps1 15 | ``` 16 | {% endraw %} -------------------------------------------------------------------------------- /docs/_includes/reference/trdl_yaml/example_result.md.liquid: -------------------------------------------------------------------------------- 1 | ```shell 2 | result 3 | ├── any-any 4 | │ └── bin 5 | │ └── trdl-example.sh 6 | └── windows-any 7 | └── bin 8 | └── trdl-example.ps1 9 | ``` -------------------------------------------------------------------------------- /docs/_includes/reference/trdl_yaml/example_trdl_yaml.md.liquid: -------------------------------------------------------------------------------- 1 | {% raw %} 2 | ```yaml 3 | dockerImage: alpine:3.13.6@sha256:e15947432b813e8ffa90165da919953e2ce850bef511a0ad1287d7cb86de84b5 4 | commands: 5 | - ./build.sh {{ .Tag }} && cp -a release-build/{{ .Tag }}/* /result 6 | ``` 7 | {% endraw %} -------------------------------------------------------------------------------- /docs/_includes/reference/trdl_yaml/example_trdl_yaml_w_secrets.md.liquid: -------------------------------------------------------------------------------- 1 | {% raw %} 2 | ```yaml 3 | dockerImage: alpine:3.13.6@sha256:e15947432b813e8ffa90165da919953e2ce850bef511a0ad1287d7cb86de84b5 4 | commands: 5 | - AWS_SHARED_CREDENTIALS_FILE=/run/secrets/aws ./build.sh {{ .Tag }} && cp -a release-build/{{ .Tag }}/* /result 6 | ``` 7 | {% endraw %} -------------------------------------------------------------------------------- /docs/_includes/reference/trdl_yaml/table.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | {{ "tableLineCounter" | reset_shared_counter }} 6 | {% for directive in site.data.trdl.directives %} 7 | {% include reference/configuration_table_directive.html directive=directive %} 8 | {% endfor %} 9 | 10 |
-------------------------------------------------------------------------------- /docs/_includes/reference/vault_plugin/configure/build/secrets.md: -------------------------------------------------------------------------------- 1 | Add a build secret. 2 | 3 | ## Add a build secret 4 | 5 | 6 | | Method | Path | 7 | |--------|------| 8 | | `POST` | `/configure/build/secrets` | 9 | 10 | ### Parameters 11 | 12 | * `data` (string, required) — Secret data. 13 | * `id` (string, required) — Secret Id. 14 | 15 | ### Responses 16 | 17 | * 200 — OK. 18 | -------------------------------------------------------------------------------- /docs/_includes/reference/vault_plugin/configure/build/secrets/id.md: -------------------------------------------------------------------------------- 1 | Delete a build secret. 2 | 3 | ## Delete a build secret 4 | 5 | 6 | | Method | Path | 7 | |--------|------| 8 | | `DELETE` | `/configure/build/secrets/:id` | 9 | 10 | ### Parameters 11 | 12 | * `id` (url pattern, required) — Secret Id. 13 | 14 | ### Responses 15 | 16 | * 204 — empty body. 17 | -------------------------------------------------------------------------------- /docs/_includes/reference/vault_plugin/configure/git_credential.md: -------------------------------------------------------------------------------- 1 | Configure Git credentials. 2 | 3 | ## Configure Git credentials 4 | 5 | 6 | | Method | Path | 7 | |--------|------| 8 | | `POST` | `/configure/git_credential` | 9 | 10 | ### Parameters 11 | 12 | * `password` (string, optional) — A Git password; Required for CREATE, UPDATE.. 13 | * `username` (string, optional) — A Git username; Required for CREATE, UPDATE.. 14 | 15 | ### Responses 16 | 17 | * 200 — OK. 18 | 19 | 20 | ## Reset Git credentials 21 | 22 | 23 | | Method | Path | 24 | |--------|------| 25 | | `DELETE` | `/configure/git_credential` | 26 | 27 | 28 | ### Responses 29 | 30 | * 204 — empty body. 31 | -------------------------------------------------------------------------------- /docs/_includes/reference/vault_plugin/configure/last_published_git_commit.md: -------------------------------------------------------------------------------- 1 | Read or delete the last published Git commit. 2 | 3 | ## Get the last published Git commit 4 | 5 | 6 | | Method | Path | 7 | |--------|------| 8 | | `GET` | `/configure/last_published_git_commit` | 9 | 10 | 11 | ### Responses 12 | 13 | * 200 — OK. 14 | 15 | 16 | ## Delete the last published Git commit 17 | 18 | 19 | | Method | Path | 20 | |--------|------| 21 | | `DELETE` | `/configure/last_published_git_commit` | 22 | 23 | 24 | ### Responses 25 | 26 | * 204 — empty body. 27 | -------------------------------------------------------------------------------- /docs/_includes/reference/vault_plugin/configure/pgp_signing_key.md: -------------------------------------------------------------------------------- 1 | Configure a PGP key for signing release artifacts. 2 | 3 | ## Get the public part of the current PGP signing key 4 | 5 | 6 | | Method | Path | 7 | |--------|------| 8 | | `GET` | `/configure/pgp_signing_key` | 9 | 10 | 11 | ### Responses 12 | 13 | * 200 — OK. 14 | 15 | 16 | ## Delete the current PGP signing key 17 | 18 | Delete the current PGP signing key (new key will be generated automatically on demand) 19 | 20 | 21 | | Method | Path | 22 | |--------|------| 23 | | `DELETE` | `/configure/pgp_signing_key` | 24 | 25 | 26 | ### Responses 27 | 28 | * 204 — empty body. 29 | -------------------------------------------------------------------------------- /docs/_includes/reference/vault_plugin/configure/trusted_pgp_public_key.md: -------------------------------------------------------------------------------- 1 | Configure trusted PGP public keys. 2 | 3 | ## Add a trusted PGP public key 4 | 5 | 6 | | Method | Path | 7 | |--------|------| 8 | | `POST` | `/configure/trusted_pgp_public_key` | 9 | 10 | ### Parameters 11 | 12 | * `name` (string, required) — Key name. 13 | * `public_key` (string, required) — Key data. 14 | 15 | ### Responses 16 | 17 | * 200 — OK. 18 | 19 | 20 | ## Get the list of trusted PGP public keys 21 | 22 | 23 | | Method | Path | 24 | |--------|------| 25 | | `GET` | `/configure/trusted_pgp_public_key` | 26 | 27 | ### Parameters 28 | 29 | * `list` (string, optional) — Return a list if `true`. 30 | 31 | ### Responses 32 | 33 | * 200 — OK. 34 | -------------------------------------------------------------------------------- /docs/_includes/reference/vault_plugin/configure/trusted_pgp_public_key/name.md: -------------------------------------------------------------------------------- 1 | Read or delete the configured trusted PGP public key. 2 | 3 | ## Get the trusted PGP public key 4 | 5 | 6 | | Method | Path | 7 | |--------|------| 8 | | `GET` | `/configure/trusted_pgp_public_key/:name` | 9 | 10 | ### Parameters 11 | 12 | * `name` (url pattern, required) — Key name. 13 | * `list` (string, optional) — Return a list if `true`. 14 | 15 | ### Responses 16 | 17 | * 200 — OK. 18 | 19 | 20 | ## Delete the trusted PGP public key 21 | 22 | 23 | | Method | Path | 24 | |--------|------| 25 | | `DELETE` | `/configure/trusted_pgp_public_key/:name` | 26 | 27 | ### Parameters 28 | 29 | * `name` (url pattern, required) — Key name. 30 | 31 | ### Responses 32 | 33 | * 204 — empty body. 34 | -------------------------------------------------------------------------------- /docs/_includes/reference/vault_plugin/publish.md: -------------------------------------------------------------------------------- 1 | Publish release channels. 2 | 3 | ## Publish release channels 4 | 5 | 6 | | Method | Path | 7 | |--------|------| 8 | | `POST` | `/publish` | 9 | 10 | ### Parameters 11 | 12 | * `git_password` (string, optional) — Git password. 13 | * `git_username` (string, optional) — Git username. 14 | 15 | ### Responses 16 | 17 | * 200 — OK. 18 | -------------------------------------------------------------------------------- /docs/_includes/reference/vault_plugin/release.md: -------------------------------------------------------------------------------- 1 | Perform a release. 2 | 3 | ## Perform a release 4 | 5 | 6 | | Method | Path | 7 | |--------|------| 8 | | `POST` | `/release` | 9 | 10 | ### Parameters 11 | 12 | * `git_password` (string, optional) — Git password. 13 | * `git_tag` (string, required) — Git tag. 14 | * `git_username` (string, optional) — Git username. 15 | 16 | ### Responses 17 | 18 | * 200 — OK. 19 | -------------------------------------------------------------------------------- /docs/_includes/reference/vault_plugin/task.md: -------------------------------------------------------------------------------- 1 | Get tasks. 2 | 3 | ## Get a list of task UUIDs 4 | 5 | 6 | | Method | Path | 7 | |--------|------| 8 | | `GET` | `/task` | 9 | 10 | 11 | ### Responses 12 | 13 | * 200 — OK. 14 | -------------------------------------------------------------------------------- /docs/_includes/reference/vault_plugin/task/configure.md: -------------------------------------------------------------------------------- 1 | Configure the task manager. 2 | 3 | ## Configure the task manager 4 | 5 | 6 | | Method | Path | 7 | |--------|------| 8 | | `POST` | `/task/configure` | 9 | 10 | ### Parameters 11 | 12 | * `task_history_limit` (integer, optional, default: `10`) — Task history limit. 13 | * `task_timeout` (integer, optional, default: `30m`) — Task timeout. 14 | 15 | ### Responses 16 | 17 | * 200 — OK. 18 | 19 | 20 | ## Get the task manager configuration 21 | 22 | 23 | | Method | Path | 24 | |--------|------| 25 | | `GET` | `/task/configure` | 26 | 27 | 28 | ### Responses 29 | 30 | * 200 — OK. 31 | -------------------------------------------------------------------------------- /docs/_includes/reference/vault_plugin/task/uuid.md: -------------------------------------------------------------------------------- 1 | Get task status. 2 | 3 | ## Get the task status 4 | 5 | 6 | | Method | Path | 7 | |--------|------| 8 | | `GET` | `/task/:uuid` | 9 | 10 | ### Parameters 11 | 12 | * `uuid` (url pattern, required) — Task UUID. 13 | 14 | ### Responses 15 | 16 | * 200 — OK. 17 | -------------------------------------------------------------------------------- /docs/_includes/reference/vault_plugin/task/uuid/cancel.md: -------------------------------------------------------------------------------- 1 | Cancel the running task. 2 | 3 | ## Cancel the running task 4 | 5 | 6 | | Method | Path | 7 | |--------|------| 8 | | `POST` | `/task/:uuid/cancel` | 9 | 10 | ### Parameters 11 | 12 | * `uuid` (url pattern, required) — Task UUID. 13 | 14 | ### Responses 15 | 16 | * 200 — OK. 17 | -------------------------------------------------------------------------------- /docs/_includes/reference/vault_plugin/task/uuid/log.md: -------------------------------------------------------------------------------- 1 | Get the task log. 2 | 3 | ## Get the task log 4 | 5 | 6 | | Method | Path | 7 | |--------|------| 8 | | `GET` | `/task/:uuid/log` | 9 | 10 | ### Parameters 11 | 12 | * `uuid` (url pattern, required) — Task UUID. 13 | 14 | ### Responses 15 | 16 | * 200 — OK. 17 | -------------------------------------------------------------------------------- /docs/_includes/security_en/intro.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | Security 6 |
7 |

trdl is designed to minimize the damage from potential attacks on the release system. The Vault secret manager, the TUF-based repository (The Update Framework), and Git are the three main components that make this possible. 8 |

9 |
10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 | -------------------------------------------------------------------------------- /docs/_includes/security_en/not-protecting.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
What trdl does not protect against
5 |
6 |
7 |
    8 |
  • 9 | 10 | 11 | 12 |

    13 | trdl cannot protect you against threats related to physical access to the host where the trdl-client is installed. 14 |

    15 |
  • 16 |
17 |
18 | 19 |
20 |
    21 |
  • 22 | 23 | 24 | 25 |

    26 | trdl cannot protect you against human errors, e.g., incorrect GPG signature quorum configuration, improper build instructions, and faulty Vault config. 27 |

    28 |
  • 29 |
30 |
31 |
32 | 33 |
34 |
35 |
36 | -------------------------------------------------------------------------------- /docs/_includes/security_en/recommendations.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
Our recommendations
5 |
6 |
    7 |
    8 |
  • Use an external authentication provider instead of the Vault root token.
  • 9 | 10 |
  • Set up an NGINX proxy to secure access to Vault by switching to HTTPS and enabling access only to certain Vault endpoints.
  • 11 | 12 |
  • Run Docker on the same host as Vault and block outside access to Docker.
  • 13 | 14 |
    15 |
    16 |
  • Do not install any other software on the virtual machine where Vault and the trdl plugin are running.
  • 17 | 18 |
  • Use the common and proven methods of protecting the OS/host.
  • 19 | 20 |
    21 |
22 |
23 |
24 |
25 |
26 | -------------------------------------------------------------------------------- /docs/_includes/security_ru/intro.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | Безопасность 6 |
7 |

trdl спроектирован так, чтобы минимизировать ущерб от потенциальных атак на систему обновления. Три основных компонента, которые за это отвечают, — менеджер секретов Vault, репозиторий на основе The Update Framework и Git.

8 |
9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |
-------------------------------------------------------------------------------- /docs/_includes/security_ru/not-protecting.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
От чего trdl не защищает
5 |
6 |
7 |
    8 |
  • 9 | 10 | 11 | 12 |

    13 | От угроз, связанных с физическим доступом к хосту, на котором установлен trdl-клиент. 14 |

    15 |
  • 16 |
17 |
18 | 19 |
20 |
    21 |
  • 22 | 23 | 24 | 25 |

    26 | От ошибок, связанных с человеческим фактором, в частности — некорректной настройки кворума GPG-подписей, сборочных инструкций, самого Vault. 27 |

    28 |
  • 29 |
30 |
31 |
32 | 33 |
34 |
35 |
-------------------------------------------------------------------------------- /docs/_includes/security_ru/recommendations.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
Наши рекомендации
5 |
6 |
    7 |
    8 |
  • Отказаться от использования root-токена Vault в пользу внешнего провайдера аутентификации.
  • 9 | 10 |
  • Организовать доступ к Vault через nginx, который обеспечивает HTTPS-соединение и предоставляет доступ только к определенным endpoint’ам Vault.
  • 11 | 12 |
  • Запускать Docker на том же хосте, что и Vault, без доступа к нему извне.
  • 13 | 14 |
    15 |
    16 |
  • Не устанавливать на виртуальной машине с Vault и плагином trdl какое-либо другое ПО.
  • 17 | 18 |
  • Не забывать про базовые методы защиты хоста и ОС.
  • 19 | 20 |
    21 |
22 |
23 |
24 |
25 |
-------------------------------------------------------------------------------- /docs/_includes/sidebar.html: -------------------------------------------------------------------------------- 1 | {%- assign sidebar = site.data.sidebars[page.sidebar].entries[site.site_lang] %} 2 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/_includes/sidebar_entry.html: -------------------------------------------------------------------------------- 1 | {%- assign entry = include.entry %} 2 | {%- assign folder_entry_class = include.folder_entry_class %} 3 | {%- assign item_entry_class = include.item_entry_class %} 4 | 5 | {%- if entry.hot == true %} 6 | {%- assign item_entry_class = item_entry_class | append: ' sidebar__item_hot' %} 7 | {%- endif %} 8 | 9 | {%- if entry.f %} 10 |
  • 11 | {{ entry.title }}{{ entry.url }} 12 | 17 |
  • 18 | {%- elsif entry.external_url %} 19 |
  • {{entry.title}}
  • 20 | {%- elsif page.url == entry.url %} 21 |
  • {{entry.title}}
  • 22 | {%- else %} 23 | {%- if page.name == '404.md' %} 24 |
  • {{entry.title}}
  • 25 | {% else %} 26 |
  • {{entry.title}}
  • 27 | {%- endif %} 28 | {%- endif %} -------------------------------------------------------------------------------- /docs/_includes/toc.html: -------------------------------------------------------------------------------- 1 | {% capture toc_headers %} 2 | {%- if page.toc_headers != null && page.toc_headers != "" -%} 3 | {{- page.toc_headers -}} 4 | {% else %} 5 | {{- "h2,h3,h4" -}} 6 | {%- endif -%} 7 | {% endcapture %} 8 | 9 | 10 | 29 | 30 |
    31 | -------------------------------------------------------------------------------- /docs/_layouts/main-page.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 |
    5 | {% include topnav.html %} 6 | 7 | {{ content }} 8 | 9 | {% include footer.html %} 10 |
    -------------------------------------------------------------------------------- /docs/_layouts/none.html: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | {{content}} -------------------------------------------------------------------------------- /docs/_layouts/page-nosidebar.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 5 |
    6 | {% include topnav.html %} 7 |
    8 |
    9 |
    10 |
    11 | {% unless page.without_auto_heading %} 12 |

    {{ page.title }}

    13 | {% endunless %} 14 |
    15 | 16 | {%- unless page.toc == false %} 17 | {%- include toc.html %} 18 | {%- endunless %} 19 | 20 | {{content}} 21 |
    22 |
    23 |
    24 |
    25 |
    26 | {% include footer.html %} 27 |
    28 | -------------------------------------------------------------------------------- /docs/_layouts/page.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: sidebar 3 | --- 4 | 5 |
    6 | {% unless page.without_auto_heading %} 7 |

    {{ page.title }}

    8 | {% endunless %} 9 | 10 |
    11 | 12 | {%- unless page.toc == false %} 13 | {%- include toc.html %} 14 | {%- endunless %} 15 | 16 | {{content}} 17 | 18 |
    19 | 20 |
    -------------------------------------------------------------------------------- /docs/_layouts/sidebar.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 5 |
    6 | {% include topnav.html %} 7 |
    8 |
    9 |
    10 | {%- include nav-breadcrumbs.html %} 11 |
    12 |
    13 | {%- include sidebar.html %} 14 |
    15 |
    16 | {{ content }} 17 |
    18 |
    19 |
    20 |
    21 |
    22 | {% include footer.html %} 23 |
    24 | -------------------------------------------------------------------------------- /docs/_plugins/custom_filters.rb: -------------------------------------------------------------------------------- 1 | module Jekyll 2 | module CustomFilters 3 | def true_relative_url(path) 4 | if !path.instance_of? String 5 | raise "true_relative_url filter failed: unexpected argument #{path}" 6 | end 7 | 8 | # remove first slash if exist 9 | page_path_relative = @context.registers[:page]["url"].gsub(%r!^/!, "") 10 | page_depth = page_path_relative.scan(%r!/!).count 11 | prefix = "" 12 | page_depth.times{ prefix = prefix + "../" } 13 | prefix + path.sub(%r!^/!, "") 14 | end 15 | 16 | # get_lang_field_or_raise_error filter returns a field from argument hash 17 | # returns nil if hash is empty 18 | # returns hash[site.site_lang] if hash has the field 19 | # returns hash["all"] if hash has the field 20 | # otherwise, raise an error 21 | def get_lang_field_or_raise_error(hash) 22 | if !(hash == nil or hash.instance_of? Hash) 23 | raise "get_lang_field_or_raise_error filter failed: unexpected argument '#{hash}'" 24 | end 25 | 26 | if hash == nil or hash.length == 0 27 | return 28 | end 29 | 30 | lang = @context.registers[:site].config["site_lang"] 31 | if hash.has_key?(lang) 32 | return hash[lang] 33 | elsif hash.has_key?("all") 34 | return hash["all"] 35 | else 36 | raise "get_lang_field_or_raise_error filter failed: the argument '#{hash}' does not have '#{lang}' or 'all' field" 37 | end 38 | end 39 | end 40 | end 41 | 42 | Liquid::Template.register_filter(Jekyll::CustomFilters) 43 | -------------------------------------------------------------------------------- /docs/_plugins/offtopic.rb: -------------------------------------------------------------------------------- 1 | module Jekyll 2 | module Offtopic 3 | class OfftopicTag < Liquid::Block 4 | @@DEFAULTS = { 5 | :title => 'Подробности', 6 | :compact => false, 7 | } 8 | 9 | def self.DEFAULTS 10 | return @@DEFAULTS 11 | end 12 | 13 | def initialize(tag_name, markup, tokens) 14 | super 15 | 16 | @config = {} 17 | override_config(@@DEFAULTS) 18 | 19 | params = markup.scan /([a-z]+)\=\"(.+?)\"/ 20 | if params.size > 0 21 | config = {} 22 | params.each do |param| 23 | config[param[0].to_sym] = param[1] 24 | end 25 | override_config(config) 26 | end 27 | end 28 | 29 | def override_config(config) 30 | config.each{ |key,value| @config[key] = value } 31 | end 32 | 33 | def render(context) 34 | content = super 35 | 36 | rendered_content = Jekyll::Converters::Markdown::KramdownParser.new(Jekyll.configuration()).convert(content) 37 | 38 | if @config[:compact] 39 | div_details_class = "details details__compact" 40 | else 41 | div_details_class = "details" 42 | end 43 | 44 | %Q( 45 |
    46 | #{@config[:title]} 47 |
    48 | #{rendered_content} 49 |
    50 |
    51 | ) 52 | end 53 | end 54 | end 55 | end 56 | 57 | Liquid::Template.register_tag('offtopic', Jekyll::Offtopic::OfftopicTag) 58 | -------------------------------------------------------------------------------- /docs/_plugins/raise_error.rb: -------------------------------------------------------------------------------- 1 | module Jekyll 2 | module ExceptionFilter 3 | def raise_error(msg) 4 | bad_file = @context.registers[:page]['path'] 5 | err_msg = "On #{bad_file}: #{msg}" 6 | raise err_msg 7 | end 8 | end 9 | end 10 | 11 | Liquid::Template.register_filter(Jekyll::ExceptionFilter) 12 | -------------------------------------------------------------------------------- /docs/_plugins/variables.rb: -------------------------------------------------------------------------------- 1 | module Jekyll 2 | module Variables 3 | def increment_shared_counter(name) 4 | @context.registers[:werf_docs_variables] ||= {} 5 | @context.registers[:werf_docs_variables][name] ||= 0 6 | 7 | old_value = @context.registers[:werf_docs_variables][name] 8 | @context.registers[:werf_docs_variables][name] += 1 9 | 10 | return old_value 11 | end 12 | 13 | def reset_shared_counter(name) 14 | @context.registers[:werf_docs_variables] ||= {} 15 | @context.registers[:werf_docs_variables][name] = 0 16 | return 17 | end 18 | end 19 | end 20 | 21 | Liquid::Template.register_filter(Jekyll::Variables) 22 | -------------------------------------------------------------------------------- /docs/css/components/_footer.scss: -------------------------------------------------------------------------------- 1 | .footer { 2 | &__wrap { 3 | padding-bottom: rem(80px); 4 | } 5 | 6 | &__wrap { 7 | display: flex; 8 | align-items: center; 9 | justify-content: space-between; 10 | } 11 | 12 | &__text { 13 | @include montserrat(regular); 14 | font-size: rem(14px); 15 | line-height: rem(17px); 16 | color: $text-color; 17 | } 18 | 19 | &__heart { 20 | width: rem(11px); 21 | height: rem(12px); 22 | } 23 | 24 | & span a { 25 | color: $text-color-accent; 26 | } 27 | 28 | &__github { 29 | display: flex; 30 | align-items: center; 31 | 32 | &--icon { 33 | width: rem(29px); 34 | height: rem(29px); 35 | padding-right: rem(12px); 36 | } 37 | 38 | &--stars { 39 | position: relative; 40 | display: flex; 41 | align-items: center; 42 | border: 1.5px solid $color-border-github; 43 | box-sizing: border-box; 44 | border-radius: rem(10px); 45 | padding: rem(pad(29, 17)) rem(9px); 46 | 47 | &::after { 48 | position: absolute; 49 | top: -1px; 50 | right: 32px; 51 | content: ''; 52 | width: 1px; 53 | height: 105%; 54 | background: $color-border-github; 55 | } 56 | 57 | & span { 58 | padding-right: rem(15px); 59 | } 60 | } 61 | 62 | &--star { 63 | width: rem(16px); 64 | height: rem(16px); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /docs/css/components/_menu-burger.scss: -------------------------------------------------------------------------------- 1 | .header { 2 | @include tablets { 3 | .burger-menu { 4 | display: block; 5 | position: relative; 6 | width: 24px; 7 | height: 10px; 8 | z-index: 3; 9 | 10 | &::before, 11 | &::after { 12 | content: ''; 13 | display: block; 14 | position: absolute; 15 | background: $color-accent; 16 | width: 100%; 17 | height: 2px; 18 | left: 0; 19 | transition: all .3s ease; 20 | } 21 | 22 | &::before { 23 | top: 0; 24 | } 25 | 26 | &::after { 27 | bottom: 0; 28 | } 29 | 30 | &.active { 31 | &::before { 32 | transform: rotate(45deg); 33 | top: 4px; 34 | } 35 | 36 | &::after { 37 | transform: rotate(-45deg); 38 | bottom: 4px; 39 | } 40 | } 41 | } 42 | 43 | .header__menu { 44 | position: fixed; 45 | top: 0; 46 | left: 0; 47 | width: 100%; 48 | height: 100%; 49 | overflow: auto; 50 | background: $color-dark; 51 | opacity: 0; 52 | margin-left: -100%; 53 | transition: all .3s ease; 54 | 55 | &.active { 56 | opacity: 1; 57 | margin-left: 0; 58 | } 59 | 60 | &__list { 61 | flex-direction: column; 62 | align-items: flex-start; 63 | padding-top: 130px; 64 | padding-left: 40px; 65 | } 66 | 67 | &__item { 68 | padding-right: 0; 69 | padding-bottom: 40px; 70 | } 71 | } 72 | } 73 | } 74 | 75 | body.lock { 76 | overflow: hidden; 77 | } -------------------------------------------------------------------------------- /docs/css/components/index/_easy-use.scss: -------------------------------------------------------------------------------- 1 | .easy-use { 2 | &__section { 3 | margin-bottom: rem(130px); 4 | color: $text-color-title-invert; 5 | position: relative; 6 | 7 | @media screen and (max-width: 1375px) { 8 | background: $color-dark; 9 | } 10 | } 11 | 12 | &__wrap { 13 | position: relative; 14 | margin: rem(50px); 15 | 16 | @include phones { 17 | margin-left: 15px; 18 | margin-right: 15px; 19 | } 20 | } 21 | 22 | &__title { 23 | // padding-bottom: rem(60px); 24 | padding-bottom: rem(35px); 25 | } 26 | 27 | &__cols { 28 | & .col__item { 29 | // @include montserrat(semi-bold); 30 | font-size: rem(14px); 31 | line-height: rem(20px); 32 | display: block; 33 | padding-left: rem(30px); 34 | 35 | &::before { 36 | content: ''; 37 | display: inline-block; 38 | width: rem(10px); 39 | height: rem(10px); 40 | border-radius: 50%; 41 | background: $color-bullets-invert; 42 | margin-left: rem(-30px); 43 | margin-right: rem(20px); 44 | } 45 | } 46 | } 47 | 48 | &__lines { 49 | position: absolute; 50 | top: 450px; 51 | right: -95px; 52 | z-index: -1; 53 | 54 | @include old-display { 55 | display: none; 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /docs/css/components/index/_how-work-trdl.scss: -------------------------------------------------------------------------------- 1 | .how-work { 2 | &__wrap { 3 | padding-bottom: rem(110px); 4 | } 5 | 6 | &__title { 7 | // padding-bottom: rem(30px); 8 | padding-bottom: rem(35px); 9 | } 10 | 11 | &__desc { 12 | max-width: 67%; 13 | padding-bottom: rem(23px); 14 | 15 | @include tablets { 16 | max-width: 100%; 17 | } 18 | } 19 | 20 | &__footnote { 21 | @include montserrat(regular-italic); 22 | font-size: rem(14px); 23 | line-height: rem(30px); 24 | color: $text-color; 25 | 26 | & a { 27 | color: $text-color-accent; 28 | text-decoration: underline; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /docs/css/components/index/_solve-that.scss: -------------------------------------------------------------------------------- 1 | .solve-that { 2 | &__section { 3 | margin-bottom: rem(70px); 4 | 5 | @media screen and (max-width: 1375px) { 6 | background: $color-accent; 7 | } 8 | } 9 | 10 | &__container { 11 | background: $color-accent; 12 | } 13 | 14 | &__wrap { 15 | margin: rem(50px); 16 | 17 | @include phones { 18 | margin-left: 15px; 19 | margin-right: 15px; 20 | } 21 | } 22 | 23 | &__title { 24 | padding-bottom: rem(35px); 25 | } 26 | 27 | &__button { 28 | &--gh { 29 | background: #fff; 30 | margin-right: rem(40px); 31 | } 32 | 33 | &--doc { 34 | border-color: #fff; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /docs/css/components/index/_whats-problems.scss: -------------------------------------------------------------------------------- 1 | .whats-problems { 2 | &__section { 3 | padding-bottom: rem(100px); 4 | } 5 | 6 | &__title { 7 | // padding-bottom: rem(80px); 8 | padding-bottom: rem(35px); 9 | } 10 | 11 | &__grid--list { 12 | grid-template-rows: 1fr; 13 | grid-template-areas: 14 | "delivery ." 15 | "delivery protection" 16 | "pack protection" 17 | "pack ."; 18 | 19 | @include tablets { 20 | grid-template-areas: 21 | "delivery" 22 | "protection" 23 | "pack" 24 | } 25 | 26 | & .card { 27 | &__pic.warning { 28 | stroke: $color-accent; 29 | } 30 | 31 | & > p { 32 | padding-bottom: rem(30px); 33 | } 34 | 35 | &--delivery { 36 | grid-area: delivery; 37 | } 38 | 39 | &--protection { 40 | grid-area: protection; 41 | 42 | & .card__list p { 43 | // @include montserrat(semi-bold); 44 | font-size: rem(14px); 45 | line-height: rem(20px); 46 | padding-left: rem(30px); 47 | color: $text-color-lightest; 48 | 49 | & span { 50 | color: $text-color-accent; 51 | } 52 | } 53 | } 54 | 55 | 56 | &--pack { 57 | grid-area: pack; 58 | height: fit-content; 59 | box-sizing: border-box; 60 | 61 | &:last-child { 62 | & .card__item:last-child { 63 | padding-bottom: 0; 64 | } 65 | } 66 | } 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /docs/css/components/security/_intro.scss: -------------------------------------------------------------------------------- 1 | .security { 2 | &.solve-that { 3 | &__section { 4 | position: relative; 5 | margin-top: rem(190px); 6 | margin-bottom: rem(140px); 7 | z-index: 0; 8 | 9 | @media screen and (max-width: 1375px) { 10 | background: $color-accent; 11 | margin-top: rem(70px); 12 | } 13 | } 14 | } 15 | 16 | & .solve-that__wrap { 17 | @include phones { 18 | margin-left: 15px; 19 | margin-right: 15px; 20 | } 21 | } 22 | 23 | &-bg__lines { 24 | position: absolute; 25 | top: rem(200px); 26 | right: rem(100px); 27 | z-index: -1; 28 | 29 | @include old-display { 30 | display: none; 31 | } 32 | } 33 | 34 | & .block__description { 35 | color: $text-color-title-invert; 36 | max-width: 80%; 37 | } 38 | } -------------------------------------------------------------------------------- /docs/css/components/security/_not-protecting.scss: -------------------------------------------------------------------------------- 1 | .security { 2 | & .architecture__cols { 3 | &--item { 4 | & .col__item .warning { 5 | stroke: $color-accent; 6 | } 7 | 8 | // &:last-child { 9 | // & .col__item { 10 | // max-width: 95%; 11 | // } 12 | // } 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /docs/css/components/security/_recommendations.scss: -------------------------------------------------------------------------------- 1 | .security { 2 | &.how-work__section { 3 | position: relative; 4 | z-index: 0; 5 | } 6 | 7 | &.how-work__title { 8 | padding-bottom: rem(35px); 9 | } 10 | 11 | .recommendations { 12 | // margin-top: rem(40px); 13 | 14 | &__list { 15 | display: flex; 16 | justify-content: space-between; 17 | 18 | @include tablets { 19 | flex-direction: column; 20 | } 21 | } 22 | 23 | &__col { 24 | width: 45%; 25 | 26 | @include laptop { 27 | width: 48%; 28 | } 29 | 30 | @include tablets { 31 | width: 100%; 32 | } 33 | } 34 | 35 | &__item { 36 | max-width: rem(380px); 37 | 38 | &::before { 39 | background: $color-accent; 40 | } 41 | 42 | @include laptop { 43 | max-width: 79%; 44 | } 45 | 46 | @include tablets { 47 | max-width: 100%; 48 | } 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /docs/css/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/css/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /docs/css/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/css/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/css/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/css/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/css/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/css/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/css/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/css/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /docs/css/main.scss: -------------------------------------------------------------------------------- 1 | @import "misc/_variables.scss"; 2 | @import "misc/_mixins.scss"; 3 | @import "misc/common.scss"; 4 | 5 | // Header 6 | @import "components/_header.scss"; 7 | @import "components/_menu-burger.scss"; 8 | 9 | // Index 10 | @import "components/index/_intro.scss"; 11 | @import "components/index/_architecture.scss"; 12 | @import "components/index/_how-work-trdl.scss"; 13 | @import "components/index/_slider.scss"; 14 | @import "components/index/_benefits.scss"; 15 | @import "components/index/_easy-use.scss"; 16 | @import "components/index/_whats-problems.scss"; 17 | @import "components/index/_solve-that.scss"; 18 | 19 | // Security 20 | @import "components/security/_intro.scss"; 21 | @import "components/security/_components.scss"; 22 | @import "components/security/_provide.scss"; 23 | @import "components/security/_not-protecting.scss"; 24 | @import "components/security/_recommendations.scss"; 25 | 26 | @import "components/_footer.scss"; -------------------------------------------------------------------------------- /docs/css/misc/_mixins.scss: -------------------------------------------------------------------------------- 1 | @function rem($px) { 2 | @return $px / 16px + 0rem; 3 | } 4 | 5 | @function pad($height, $lh) { 6 | @return ($height - $lh)/2 + 0px; 7 | } 8 | 9 | @mixin montserrat($style: regular) { 10 | font-family: 'Montserrat', sans-serif; 11 | 12 | @if $style == regular { 13 | font-weight: 400; 14 | } 15 | 16 | @if $style == regular-italic { 17 | font-weight: 400; 18 | font-style: italic; 19 | } 20 | 21 | @if $style == semi-bold { 22 | font-weight: 600; 23 | } 24 | 25 | @if $style == bold { 26 | font-weight: 700; 27 | } 28 | } 29 | 30 | @mixin four-k { 31 | @media screen and (min-width: 1920px) { 32 | @content; 33 | } 34 | } 35 | 36 | @mixin old-display { 37 | @media screen and (max-width: 1220px) { 38 | @content; 39 | } 40 | } 41 | 42 | @mixin laptop { 43 | @media screen and (max-width: 960px) { 44 | @content; 45 | } 46 | } 47 | 48 | @mixin tablets { 49 | @media screen and (max-width: 768px) { 50 | @content; 51 | } 52 | } 53 | 54 | @mixin phablet { 55 | @media screen and (max-width: 600px) { 56 | @content; 57 | } 58 | } 59 | 60 | @mixin phones { 61 | @media screen and (max-width: 480px) { 62 | @content; 63 | } 64 | } -------------------------------------------------------------------------------- /docs/css/misc/_variables.scss: -------------------------------------------------------------------------------- 1 | $color-bg: #F5F5F5; 2 | $color-accent: #A41FE2; 3 | $color-dark: #333037; 4 | 5 | $color-bg-accent: $color-accent; 6 | $color-bg-code: rgba(164, 31, 226, .1); 7 | $color-bg-footnote: rgba(255, 255, 255, .1); 8 | $color-bg-footnote-dark: rgba(51, 48, 55, .075); 9 | 10 | $color-border-github: #C4C4C4; 11 | 12 | $text-color: rgba(51, 48, 55, 0.7); 13 | $text-color-light: rgba(51, 48, 55, 0.4); 14 | $text-color-lightest: rgba(51, 48, 55, 0.2); 15 | $text-color-title: $color-dark; 16 | $text-color-title-invert: #fff; 17 | $text-color-title-invert-light: rgba(255, 255, 255, 0.7); 18 | $text-color-accent: $color-accent; 19 | 20 | $color-bullets: $text-color-lightest; 21 | $color-bullets-invert: $color-accent; -------------------------------------------------------------------------------- /docs/css/tab.css: -------------------------------------------------------------------------------- 1 | .tabs { 2 | display: flex; 3 | } 4 | 5 | .tabs__btn { 6 | display: inline-block; 7 | height: 28px; 8 | padding: 0 15px; 9 | border-radius: 10px; 10 | text-decoration: none !important; 11 | font-size: 14px; 12 | line-height: 24px; 13 | font-weight: 600; 14 | font-style: normal; 15 | font-stretch: normal; 16 | letter-spacing: normal; 17 | box-sizing: border-box; 18 | margin: 10px; 19 | margin-left: 0; 20 | 21 | -webkit-touch-callout: none; 22 | -webkit-user-select: none; 23 | -khtml-user-select: none; 24 | -moz-user-select: none; 25 | -ms-user-select: none; 26 | user-select: none; 27 | } 28 | 29 | .tabs__btn:not(.active) { 30 | border: 2px solid rgba(164, 31, 226, 0.1); 31 | color: #a41fe2; 32 | } 33 | 34 | .tabs__btn:not(.active):hover { 35 | border: 2px solid rgba(164, 31, 226, 0.3); 36 | } 37 | 38 | .tabs__btn.active { 39 | border: 2px solid #a41fe2; 40 | background-color: #a41fe2; 41 | color: white; 42 | } 43 | 44 | .tabs__content { 45 | display: none; 46 | } 47 | 48 | .tabs__content.active { 49 | display: block; 50 | } 51 | -------------------------------------------------------------------------------- /docs/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /docs/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /docs/images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/images/404.png -------------------------------------------------------------------------------- /docs/images/arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/images/backgrounds/community.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/images/backgrounds/feature_special.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/images/backgrounds/features.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/images/backgrounds/presentation.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/images/backgrounds/stats.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Path 3 Copy 2 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/images/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/images/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /docs/images/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/images/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /docs/images/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/images/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /docs/images/favicon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #ffffff 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/images/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/images/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /docs/images/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/images/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /docs/images/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/images/favicon/favicon.ico -------------------------------------------------------------------------------- /docs/images/favicon/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/images/favicon/mstile-150x150.png -------------------------------------------------------------------------------- /docs/images/favicon/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "short_name": "", 4 | "icons": [ 5 | { 6 | "src": "/images/favicon/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/images/favicon/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /docs/images/icons/bullet.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Rectangle 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/images/icons/check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | Group 7 9 | Created with Sketch. 10 | 11 | 12 | -------------------------------------------------------------------------------- /docs/images/icons/discourse.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /docs/images/icons/discussions.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /docs/images/icons/dropdown.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /docs/images/icons/heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/images/icons/heart.png -------------------------------------------------------------------------------- /docs/images/icons/heart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ❤️ 5 | Created with Sketch. 6 | 7 | 8 | 9 | ❤️ 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /docs/images/icons/rss.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/images/icons/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/images/icons/star.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Star Copy 2 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/images/icons/telegram.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Combined Shape 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/images/icons/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /docs/images/share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/images/share.png -------------------------------------------------------------------------------- /docs/images/telegram.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 11 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /docs/images/werf-schema.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/werf/trdl/8d7cde7a502c7240ddfe870aacd3aae59a07119a/docs/images/werf-schema.png -------------------------------------------------------------------------------- /docs/jekyll.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM jekyll/builder:3 2 | 3 | COPY Gemfile Gemfile.lock ./ 4 | 5 | RUN gem install bundler -v 2.2.28 && bundle install -------------------------------------------------------------------------------- /docs/js/channels.js: -------------------------------------------------------------------------------- 1 | window.releasesInfo = JSON.parse('{"channels":{"ts":"1576153516","date":"2019-12-12T12:25:16Z","group":"1.0","channels":[{"name":"ea","version":"v1.0.6-rc.7"}]},"orderedChannels":["rock-solid","stable","ea","beta","alpha"]}'); 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/js/details.js: -------------------------------------------------------------------------------- 1 | $( document ).ready(function() { 2 | $('.details__summary').on('click tap', function() { 3 | $(this).closest('.details').toggleClass('active'); 4 | }); 5 | }); -------------------------------------------------------------------------------- /docs/js/jquery.ba-throttle-debounce.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery throttle / debounce - v1.1 - 3/7/2010 3 | * http://benalman.com/projects/jquery-throttle-debounce-plugin/ 4 | * 5 | * Copyright (c) 2010 "Cowboy" Ben Alman 6 | * Dual licensed under the MIT and GPL licenses. 7 | * http://benalman.com/about/license/ 8 | */ 9 | (function(b,c){var $=b.jQuery||b.Cowboy||(b.Cowboy={}),a;$.throttle=a=function(e,f,j,i){var h,d=0;if(typeof f!=="boolean"){i=j;j=f;f=c}function g(){var o=this,m=+new Date()-d,n=arguments;function l(){d=+new Date();j.apply(o,n)}function k(){h=c}if(i&&!h){l()}h&&clearTimeout(h);if(i===c&&m>e){l()}else{if(f!==true){h=setTimeout(i?k:l,i===c?e-m:e)}}}if($.guid){g.guid=j.guid=j.guid||$.guid++}return g};$.debounce=function(d,e,f){return f===c?a(d,e,false):a(d,f,e!==false)}})(this); -------------------------------------------------------------------------------- /docs/js/jquery.localScroll.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2014 Ariel Flesler - afleslergmailcom | http://flesler.blogspot.com 3 | * Licensed under MIT 4 | * @author Ariel Flesler 5 | * @version 1.3.5 6 | */ 7 | ;(function(a){if(typeof define==='function'&&define.amd){define(['jquery'],a)}else{a(jQuery)}}(function($){var g=location.href.replace(/#.*/,'');var h=$.localScroll=function(a){$('body').localScroll(a)};h.defaults={duration:1000,axis:'y',event:'click',stop:true,target:window};$.fn.localScroll=function(a){a=$.extend({},h.defaults,a);if(a.hash&&location.hash){if(a.target)window.scrollTo(0,0);scroll(0,location,a)}return a.lazy?this.on(a.event,'a,area',function(e){if(filter.call(this)){scroll(e,this,a)}}):this.find('a,area').filter(filter).bind(a.event,function(e){scroll(e,this,a)}).end().end();function filter(){return!!this.href&&!!this.hash&&this.href.replace(this.hash,'')==g&&(!a.filter||$(this).is(a.filter))}};h.hash=function(){};function scroll(e,a,b){var c=a.hash.slice(1),elem=document.getElementById(c)||document.getElementsByName(c)[0];if(!elem)return;if(e)e.preventDefault();var d=$(b.target);if(b.lock&&d.is(':animated')||b.onBefore&&b.onBefore(e,elem,d)===false)return;if(b.stop)d._scrollable().stop(true);if(b.hash){var f=elem.id===c?'id':'name',$a=$(' ').attr(f,c).css({position:'absolute',top:$(window).scrollTop(),left:$(window).scrollLeft()});elem[f]='';$('body').prepend($a);location.hash=a.hash;$a.remove();elem[f]=c}d.scrollTo(elem,b).trigger('notify.serialScroll',[elem])};return h})); -------------------------------------------------------------------------------- /docs/js/menu-burger.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', () => { 2 | const burger = document.querySelector('.burger-menu'); 3 | const nav = document.querySelector('.header__menu'); 4 | const body = document.querySelector('body'); 5 | 6 | burger.addEventListener('click', () => { 7 | burger.classList.toggle('active'); 8 | nav.classList.toggle('active'); 9 | body.classList.toggle('lock'); 10 | }) 11 | }) -------------------------------------------------------------------------------- /docs/js/popup.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('[data-open-popup]').on('click', function(e) { 3 | e.preventDefault(); 4 | $('[data-popup="' + $(this).data('open-popup') + '"]').addClass('active'); 5 | }); 6 | $('[data-close-popup]').on('click', function(e) { 7 | e.preventDefault(); 8 | $('[data-popup]').removeClass('active'); 9 | }); 10 | $(document).on('keyup',function(evt) { 11 | if (evt.keyCode === 27) { 12 | $('[data-popup]').removeClass('active'); 13 | } 14 | }); 15 | $('[data-popup] .popup__content').click(function(e) { 16 | e.stopPropagation(); 17 | }); 18 | $('[data-popup]').click(function() { 19 | $('[data-popup]').removeClass('active'); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /docs/js/slider.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function(){ 2 | const sel = $('.slider__nav'); 3 | const nav = $('.slider__navigation'); 4 | 5 | $('.slider__wrap').slick({ 6 | autoplay: false, 7 | draggable: false, 8 | infinite: true, 9 | dots: true, 10 | speed: 0, 11 | dotsClass: 'slider__dots', 12 | appendArrows: sel, 13 | appendDots: sel, 14 | prevArrow: ``, 15 | nextArrow: `` 16 | }); 17 | 18 | $('.slider__wrapper').slick({ 19 | autoplay: false, 20 | draggable: false, 21 | infinite: true, 22 | dots: true, 23 | speed: 0, 24 | dotsClass: 'slider__dots', 25 | appendArrows: nav, 26 | appendDots: nav, 27 | prevArrow: ``, 28 | nextArrow: `` 29 | }); 30 | }); -------------------------------------------------------------------------------- /docs/js/tab.js: -------------------------------------------------------------------------------- 1 | function openTab(evt, linksClass, contentClass, contentId) { 2 | var i, tabcontent, tablinks; 3 | 4 | tabcontent = document.getElementsByClassName(contentClass); 5 | for (i = 0; i < tabcontent.length; i++) { 6 | tabcontent[i].style.display = "none"; 7 | } 8 | 9 | tablinks = document.getElementsByClassName(linksClass); 10 | for (i = 0; i < tablinks.length; i++) { 11 | tablinks[i].className = tablinks[i].className.replace(" active", ""); 12 | } 13 | 14 | document.getElementById(contentId).style.display = "block"; 15 | evt.currentTarget.className += " active"; 16 | } 17 | -------------------------------------------------------------------------------- /docs/js/tippy.js: -------------------------------------------------------------------------------- 1 | window.addEventListener('DOMContentLoaded', ()=> { 2 | tippy('.footnote', { 3 | interactive: true, 4 | interactiveDebounce: 75, 5 | allowHTML: true, 6 | content(reference) { 7 | const title = reference.getAttribute('title'); 8 | reference.removeAttribute('title'); 9 | return title; 10 | }, 11 | }); 12 | 13 | // tippy('#vault__en', { 14 | // interactive: true, 15 | // interactiveDebounce: 75, 16 | // allowHTML: true, 17 | // content: "HashiCorp's Vault is a secret management tool. In trdl, we use a custom Vault plugin tailored for secure package delivery. Learn more about Vault." 18 | // }); 19 | 20 | tippy('.required', { 21 | content(reference) { 22 | const title = reference.getAttribute('title'); 23 | reference.removeAttribute('title'); 24 | return title; 25 | }, 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /docs/pages_en/404.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Page Not Found" 3 | search: exclude 4 | sitemap_include: false 5 | permalink: 404.html 6 | breadcrumbs: none 7 | editme_button: false 8 | layout: main-page 9 | --- 10 | 11 |
    12 |
    13 |
    14 |
    Sorry, the page you were looking for does not exist.
    15 |
    16 |
    17 |
    18 | -------------------------------------------------------------------------------- /docs/pages_en/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: Deliver your software continuously & securely 3 | permalink: / 4 | layout: main-page 5 | --- 6 | 7 | 8 | {% include index_en/intro.html %} 9 | 10 | {% include index_en/whats-problems.html %} 11 | 12 | {% include index_en/architecture.html %} 13 | 14 | {% include index_en/how-work-trdl.html %} 15 | 16 | {% include index_en/slider-release.html %} 17 | 18 | {% include index_en/slider.html %} 19 | 20 | {% include index_en/benefits.html %} -------------------------------------------------------------------------------- /docs/pages_en/reference/cli/overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Overview 3 | permalink: reference/cli/overview.html 4 | toc: false 5 | --- 6 | 7 | Configuration commands: 8 | - [trdl add]({{ "/reference/cli/trdl_add.html" | true_relative_url }}) — {% include /reference/cli/trdl_add.short.md %}. 9 | - [trdl remove]({{ "/reference/cli/trdl_remove.html" | true_relative_url }}) — {% include /reference/cli/trdl_remove.short.md %}. 10 | - [trdl list]({{ "/reference/cli/trdl_list.html" | true_relative_url }}) — {% include /reference/cli/trdl_list.short.md %}. 11 | - [trdl set-default-channel]({{ "/reference/cli/trdl_set_default_channel.html" | true_relative_url }}) — {% include /reference/cli/trdl_set_default_channel.short.md %}. 12 | 13 | Main commands: 14 | - [trdl use]({{ "/reference/cli/trdl_use.html" | true_relative_url }}) — {% include /reference/cli/trdl_use.short.md %}. 15 | 16 | Advanced commands: 17 | - [trdl update]({{ "/reference/cli/trdl_update.html" | true_relative_url }}) — {% include /reference/cli/trdl_update.short.md %}. 18 | - [trdl exec]({{ "/reference/cli/trdl_exec.html" | true_relative_url }}) — {% include /reference/cli/trdl_exec.short.md %}. 19 | - [trdl dir-path]({{ "/reference/cli/trdl_dir_path.html" | true_relative_url }}) — {% include /reference/cli/trdl_dir_path.short.md %}. 20 | - [trdl bin-path]({{ "/reference/cli/trdl_bin_path.html" | true_relative_url }}) — {% include /reference/cli/trdl_bin_path.short.md %}. 21 | -------------------------------------------------------------------------------- /docs/pages_en/reference/cli/trdl_add.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: trdl add 3 | permalink: reference/cli/trdl_add.html 4 | --- 5 | 6 | {% include /reference/cli/trdl_add.md %} 7 | -------------------------------------------------------------------------------- /docs/pages_en/reference/cli/trdl_bin_path.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: trdl bin-path 3 | permalink: reference/cli/trdl_bin_path.html 4 | --- 5 | 6 | {% include /reference/cli/trdl_bin_path.md %} 7 | -------------------------------------------------------------------------------- /docs/pages_en/reference/cli/trdl_dir_path.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: trdl dir-path 3 | permalink: reference/cli/trdl_dir_path.html 4 | --- 5 | 6 | {% include /reference/cli/trdl_dir_path.md %} 7 | -------------------------------------------------------------------------------- /docs/pages_en/reference/cli/trdl_exec.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: trdl exec 3 | permalink: reference/cli/trdl_exec.html 4 | --- 5 | 6 | {% include /reference/cli/trdl_exec.md %} 7 | -------------------------------------------------------------------------------- /docs/pages_en/reference/cli/trdl_list.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: trdl list 3 | permalink: reference/cli/trdl_list.html 4 | --- 5 | 6 | {% include /reference/cli/trdl_list.md %} 7 | -------------------------------------------------------------------------------- /docs/pages_en/reference/cli/trdl_remove.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: trdl remove 3 | permalink: reference/cli/trdl_remove.html 4 | --- 5 | 6 | {% include /reference/cli/trdl_remove.md %} 7 | -------------------------------------------------------------------------------- /docs/pages_en/reference/cli/trdl_set_default_channel.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: trdl set-default-channel 3 | permalink: reference/cli/trdl_set_default_channel.html 4 | --- 5 | 6 | {% include /reference/cli/trdl_set_default_channel.md %} 7 | -------------------------------------------------------------------------------- /docs/pages_en/reference/cli/trdl_update.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: trdl update 3 | permalink: reference/cli/trdl_update.html 4 | --- 5 | 6 | {% include /reference/cli/trdl_update.md %} 7 | -------------------------------------------------------------------------------- /docs/pages_en/reference/cli/trdl_use.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: trdl use 3 | permalink: reference/cli/trdl_use.html 4 | --- 5 | 6 | {% include /reference/cli/trdl_use.md %} 7 | -------------------------------------------------------------------------------- /docs/pages_en/reference/trdl_channels_yaml.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: trdl_channels.yaml 3 | permalink: reference/trdl_channels_yaml.html 4 | toc: false 5 | --- 6 | 7 | The `trdl_channels.yaml` configuration contains groups, release channels, and versions. 8 | 9 | When publishing, trdl reads `trdl_channels.yaml` from the default Git repository branch (unless explicitly overridden by the vault plugin configuration) and applies the changes. Updates then become available to users. 10 | 11 | {% include reference/trdl_channels_yaml/table.html %} 12 | 13 | ## Example 14 | 15 | ```yaml 16 | groups: 17 | - name: 1.1 18 | channels: 19 | - name: alpha 20 | version: 1.1.25 21 | - name: beta 22 | version: 1.1.24 23 | - name: ea 24 | version: 1.1.23 25 | - name: stable 26 | version: 1.1.20 27 | - name: rock-solid 28 | version: 1.1.12 29 | - name: 1.2 30 | channels: 31 | - name: alpha 32 | version: 1.2.39 33 | - name: beta 34 | version: 1.2.38 35 | - name: ea 36 | version: 1.2.30 37 | ``` 38 | -------------------------------------------------------------------------------- /docs/pages_en/reference/vault_plugin/configure.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: /configure 3 | permalink: reference/vault_plugin/configure.html 4 | --- 5 | 6 | {% include /reference/vault_plugin/configure.md %} 7 | -------------------------------------------------------------------------------- /docs/pages_en/reference/vault_plugin/configure/build/secrets.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: /configure/build/secrets 3 | permalink: reference/vault_plugin/configure/build/secrets.html 4 | --- 5 | 6 | {% include /reference/vault_plugin/configure/build/secrets.md %} 7 | -------------------------------------------------------------------------------- /docs/pages_en/reference/vault_plugin/configure/build/secrets/id.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: /configure/build/secrets/:id 3 | permalink: reference/vault_plugin/configure/build/secrets/id.html 4 | --- 5 | 6 | {% include /reference/vault_plugin/configure/build/secrets/id.md %} 7 | -------------------------------------------------------------------------------- /docs/pages_en/reference/vault_plugin/configure/git_credential.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: /configure/git_credential 3 | permalink: reference/vault_plugin/configure/git_credential.html 4 | --- 5 | 6 | {% include /reference/vault_plugin/configure/git_credential.md %} 7 | -------------------------------------------------------------------------------- /docs/pages_en/reference/vault_plugin/configure/last_published_git_commit.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: /configure/last_published_git_commit 3 | permalink: reference/vault_plugin/configure/last_published_git_commit.html 4 | --- 5 | 6 | {% include /reference/vault_plugin/configure/last_published_git_commit.md %} 7 | -------------------------------------------------------------------------------- /docs/pages_en/reference/vault_plugin/configure/pgp_signing_key.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: /configure/pgp_signing_key 3 | permalink: reference/vault_plugin/configure/pgp_signing_key.html 4 | --- 5 | 6 | {% include /reference/vault_plugin/configure/pgp_signing_key.md %} 7 | -------------------------------------------------------------------------------- /docs/pages_en/reference/vault_plugin/configure/trusted_pgp_public_key.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: /configure/trusted_pgp_public_key 3 | permalink: reference/vault_plugin/configure/trusted_pgp_public_key.html 4 | --- 5 | 6 | {% include /reference/vault_plugin/configure/trusted_pgp_public_key.md %} 7 | -------------------------------------------------------------------------------- /docs/pages_en/reference/vault_plugin/configure/trusted_pgp_public_key/name.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: /configure/trusted_pgp_public_key/:name 3 | permalink: reference/vault_plugin/configure/trusted_pgp_public_key/name.html 4 | --- 5 | 6 | {% include /reference/vault_plugin/configure/trusted_pgp_public_key/name.md %} 7 | -------------------------------------------------------------------------------- /docs/pages_en/reference/vault_plugin/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Overview 3 | permalink: reference/vault_plugin/index.html 4 | --- 5 | 6 | {% include /reference/vault_plugin/index.md %} 7 | -------------------------------------------------------------------------------- /docs/pages_en/reference/vault_plugin/publish.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: /publish 3 | permalink: reference/vault_plugin/publish.html 4 | --- 5 | 6 | {% include /reference/vault_plugin/publish.md %} 7 | -------------------------------------------------------------------------------- /docs/pages_en/reference/vault_plugin/release.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: /release 3 | permalink: reference/vault_plugin/release.html 4 | --- 5 | 6 | {% include /reference/vault_plugin/release.md %} 7 | -------------------------------------------------------------------------------- /docs/pages_en/reference/vault_plugin/task.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: /task 3 | permalink: reference/vault_plugin/task.html 4 | --- 5 | 6 | {% include /reference/vault_plugin/task.md %} 7 | -------------------------------------------------------------------------------- /docs/pages_en/reference/vault_plugin/task/configure.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: /task/configure 3 | permalink: reference/vault_plugin/task/configure.html 4 | --- 5 | 6 | {% include /reference/vault_plugin/task/configure.md %} 7 | -------------------------------------------------------------------------------- /docs/pages_en/reference/vault_plugin/task/uuid.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: /task/:uuid 3 | permalink: reference/vault_plugin/task/uuid.html 4 | --- 5 | 6 | {% include /reference/vault_plugin/task/uuid.md %} 7 | -------------------------------------------------------------------------------- /docs/pages_en/reference/vault_plugin/task/uuid/cancel.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: /task/:uuid/cancel 3 | permalink: reference/vault_plugin/task/uuid/cancel.html 4 | --- 5 | 6 | {% include /reference/vault_plugin/task/uuid/cancel.md %} 7 | -------------------------------------------------------------------------------- /docs/pages_en/reference/vault_plugin/task/uuid/log.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: /task/:uuid/log 3 | permalink: reference/vault_plugin/task/uuid/log.html 4 | --- 5 | 6 | {% include /reference/vault_plugin/task/uuid/log.md %} 7 | -------------------------------------------------------------------------------- /docs/pages_en/security.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: Security 3 | permalink: security.html 4 | layout: main-page 5 | --- 6 | 7 | 8 | {% include security_en/intro.html %} 9 | 10 | {% include security_en/components.html %} 11 | 12 | {% include security_en/provide.html %} 13 | 14 | {% include security_en/not-protecting.html %} 15 | 16 | {% include security_en/recommendations.html %} 17 | -------------------------------------------------------------------------------- /docs/pages_ru/404.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Страница не найдена" 3 | search: exclude 4 | sitemap_include: false 5 | permalink: 404.html 6 | breadcrumbs: none 7 | editme_button: false 8 | layout: main-page 9 | --- 10 | 11 |
    12 |
    13 |
    14 |
    Извините, но страница, которую вы искали, отсутствует.
    15 |
    16 |
    17 |
    18 | 19 | -------------------------------------------------------------------------------- /docs/pages_ru/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: Инструмент безопасной доставки вашего кода пользователям 3 | permalink: / 4 | layout: main-page 5 | --- 6 | 7 | 8 | {% include index_ru/intro.html %} 9 | 10 | {% include index_ru/whats-problems.html %} 11 | 12 | {% include index_ru/architecture.html %} 13 | 14 | {% include index_ru/how-work-trdl.html %} 15 | 16 | {% include index_ru/slider-release.html %} 17 | 18 | {% include index_ru/slider.html %} 19 | 20 | {% include index_ru/benefits.html %} -------------------------------------------------------------------------------- /docs/pages_ru/reference/cli: -------------------------------------------------------------------------------- 1 | ../../pages_en/reference/cli/ -------------------------------------------------------------------------------- /docs/pages_ru/reference/trdl_channels_yaml.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: trdl_channels.yaml 3 | permalink: reference/trdl_channels_yaml.html 4 | toc: false 5 | --- 6 | 7 | Конфигурация `trdl_channels.yaml` содержит группы, каналы обновлений и версии. 8 | 9 | При публикации trdl читает `trdl_channels.yaml` из ветки Git-репозитория по умолчанию, если это явно не переопределено при конфигурации vault-плагина, а затем применяет изменения. Обновления становятся доступными пользователям. 10 | 11 | {% include reference/trdl_channels_yaml/table.html %} 12 | 13 | ## Пример 14 | 15 | ```yaml 16 | groups: 17 | - name: 1.1 18 | channels: 19 | - name: alpha 20 | version: 1.1.25 21 | - name: beta 22 | version: 1.1.24 23 | - name: ea 24 | version: 1.1.23 25 | - name: stable 26 | version: 1.1.20 27 | - name: rock-solid 28 | version: 1.1.12 29 | - name: 1.2 30 | channels: 31 | - name: alpha 32 | version: 1.2.39 33 | - name: beta 34 | version: 1.2.38 35 | - name: ea 36 | version: 1.2.30 37 | ``` 38 | -------------------------------------------------------------------------------- /docs/pages_ru/reference/vault_plugin: -------------------------------------------------------------------------------- 1 | ../../pages_en/reference/vault_plugin/ -------------------------------------------------------------------------------- /docs/pages_ru/security.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: Безопасность 3 | permalink: security.html 4 | layout: main-page 5 | --- 6 | 7 | 8 | {% include security_ru/intro.html %} 9 | 10 | {% include security_ru/components.html %} 11 | 12 | {% include security_ru/provide.html %} 13 | 14 | {% include security_ru/not-protecting.html %} 15 | 16 | {% include security_ru/recommendations.html %} 17 | -------------------------------------------------------------------------------- /e2e/Taskfile.yaml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | silent: true 4 | 5 | tasks: 6 | format: 7 | desc: 'Run all code formatters. Important vars: "paths".' 8 | run: once 9 | cmds: 10 | - task: format:gci 11 | - task: format:gofumpt 12 | 13 | format:gci: 14 | desc: 'Format code with gci. Important vars: "paths".' 15 | cmds: 16 | - gci write -s standard -s default -s 'Prefix(github.com/werf/)' {{.CLI_ARGS}} {{.paths | default "tests/" }} 17 | 18 | format:gofumpt: 19 | desc: 'Format code with gofumpt. Important vars: "paths".' 20 | cmds: 21 | - gofumpt -extra -w {{.CLI_ARGS}} {{.paths | default "tests/"}} 22 | 23 | lint: 24 | desc: 'Run all linters in parallel. Important vars: "paths".' 25 | deps: 26 | - lint:golangci-lint 27 | 28 | lint:golangci-lint:go: 29 | desc: 'Lint with golangci-lint. Important vars: "paths".' 30 | cmds: 31 | - golangci-lint run {{.CLI_ARGS}} --config ../.golangci.yaml {{.paths | default "./..."}} 32 | env: 33 | CGO_ENABLED: "0" 34 | 35 | _lint:golangci-lint:go: 36 | deps: 37 | - task: lint:golangci-lint:go 38 | vars: 39 | paths: "{{.paths}}" 40 | 41 | lint:golangci-lint: 42 | desc: 'Lint with golangci-lint. Important vars: "paths".' 43 | deps: 44 | - task: _lint:golangci-lint:go 45 | vars: 46 | paths: "{{.paths}}" 47 | 48 | test:e2e: 49 | desc: "Run client e2e test." 50 | cmd: ginkgo -p --keep-going --cover --coverpkg=../client/...,../server/... --output-dir={{.outputDir}} ./... 51 | vars: 52 | outputDir: '{{.outputDir | default "../tests_coverage/e2e" }}' 53 | -------------------------------------------------------------------------------- /e2e/tests/client/_fixtures/tuf_repo/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.20.2 as tuf_repo 2 | 3 | RUN go install github.com/theupdateframework/go-tuf/cmd/tuf@v0.5.2 4 | 5 | RUN mkdir /workspace 6 | WORKDIR /workspace 7 | 8 | ENV TUF_ROOT_PASSPHRASE="foobar" \ 9 | TUF_TARGETS_PASSPHRASE="foobar" \ 10 | TUF_SNAPSHOT_PASSPHRASE="foobar" \ 11 | TUF_TIMESTAMP_PASSPHRASE="foobar" 12 | RUN tuf gen-key --expires 9999 root \ 13 | && tuf gen-key --expires 9999 targets \ 14 | && tuf gen-key --expires 9999 snapshot \ 15 | && tuf gen-key --expires 9999 timestamp \ 16 | && tuf sign root.json 17 | 18 | COPY staged /workspace/staged 19 | RUN find staged/targets -type f -print0 | xargs -0 -n1 | sed -e "s|^staged/targets/||" | tuf add \ 20 | && tuf snapshot \ 21 | && tuf timestamp \ 22 | && tuf commit 23 | 24 | FROM halverneus/static-file-server:v1.8.8 25 | COPY --from=tuf_repo /workspace/repository /web 26 | -------------------------------------------------------------------------------- /e2e/tests/client/_fixtures/tuf_repo/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | server: 3 | build: . 4 | ports: 5 | - "8080" 6 | -------------------------------------------------------------------------------- /e2e/tests/client/_fixtures/tuf_repo/staged/targets/channels/0/alpha: -------------------------------------------------------------------------------- 1 | v0.0.2 2 | -------------------------------------------------------------------------------- /e2e/tests/client/_fixtures/tuf_repo/staged/targets/channels/0/beta: -------------------------------------------------------------------------------- 1 | v0.0.2 2 | -------------------------------------------------------------------------------- /e2e/tests/client/_fixtures/tuf_repo/staged/targets/channels/0/ea: -------------------------------------------------------------------------------- 1 | v0.0.2 2 | -------------------------------------------------------------------------------- /e2e/tests/client/_fixtures/tuf_repo/staged/targets/channels/0/rock-solid: -------------------------------------------------------------------------------- 1 | v0.0.1 2 | -------------------------------------------------------------------------------- /e2e/tests/client/_fixtures/tuf_repo/staged/targets/channels/0/stable: -------------------------------------------------------------------------------- 1 | v0.0.1 2 | -------------------------------------------------------------------------------- /e2e/tests/client/_fixtures/tuf_repo/staged/targets/releases/v0.0.1/any-any/bin/script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "v0.0.1" 4 | -------------------------------------------------------------------------------- /e2e/tests/client/_fixtures/tuf_repo/staged/targets/releases/v0.0.1/windows-any/bin/script.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | echo "v0.0.1" 3 | -------------------------------------------------------------------------------- /e2e/tests/client/_fixtures/tuf_repo/staged/targets/releases/v0.0.2/any-any/bin/script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "v0.0.2" 4 | -------------------------------------------------------------------------------- /e2e/tests/client/_fixtures/tuf_repo/staged/targets/releases/v0.0.2/windows-any/bin/script.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | echo "v0.0.2" 3 | -------------------------------------------------------------------------------- /e2e/tests/flow/_fixtures/complete_cycle/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | networks: 2 | minio-net: 3 | 4 | services: 5 | minio: 6 | image: minio/minio@sha256:36433a735d87bbc2fed55674b3af936b57259f5cd2c544e869b8276d4d22b590 7 | ports: 8 | - "9000" 9 | command: server /data 10 | networks: 11 | - minio-net 12 | 13 | mc: 14 | image: minio/mc@sha256:f78c05169b54f191ab407a8e4d746a2b1f65a047936ba0e51885504912c9595e 15 | depends_on: 16 | - minio 17 | environment: 18 | MC_HOST_main: http://minioadmin:minioadmin@minio:9000 19 | networks: 20 | - minio-net 21 | -------------------------------------------------------------------------------- /e2e/tests/flow/_fixtures/complete_cycle/trdl.yaml: -------------------------------------------------------------------------------- 1 | dockerImage: alpine@sha256:e1c082e3d3c45cccac829840a25941e679c25d438cc8412c2fa221cf1a824e6a 2 | commands: 3 | - '[ "$(cat /run/secrets/secretId0-test)" = "secretData" ] || (echo "output does not match the expected value" && exit 1)' 4 | - '[ "$(cat /run/secrets/secretId1-test)" = "secretData" ] || (echo "output does not match the expected value" && exit 1)' 5 | - mkdir -p /result/any-any/bin 6 | - printf "echo {{ .Tag }}\n" > /result/any-any/bin/script.sh 7 | - mkdir -p /result/windows-any/bin 8 | - printf "@echo off\necho {{ .Tag }}\n" > /result/windows-any/bin/script.bat 9 | -------------------------------------------------------------------------------- /e2e/tests/flow/_fixtures/pgp_keys: -------------------------------------------------------------------------------- 1 | ../../../../server/pkg/git/_fixtures/pgp_keys -------------------------------------------------------------------------------- /release/cmd/trdl-vault/common/cmd_data.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import "time" 4 | 5 | type CmdData struct { 6 | Address *string 7 | Token *string 8 | Retry *bool 9 | MaxAttempts *int 10 | Delay *time.Duration 11 | LogLevel *string 12 | LogFormat *string 13 | } 14 | -------------------------------------------------------------------------------- /release/cmd/trdl-vault/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/gookit/color" 8 | "github.com/spf13/cobra" 9 | 10 | "github.com/werf/trdl/release/cmd/trdl-vault/commands" 11 | ) 12 | 13 | func main() { 14 | cmd := &cobra.Command{ 15 | Use: "trdl-vault", 16 | Short: "Trdl CLI for Vault operations", 17 | } 18 | 19 | cmd.AddCommand(commands.CreateCommands()...) 20 | cmd.SilenceErrors = true 21 | 22 | if err := cmd.Execute(); err != nil { 23 | msg := fmt.Sprintf("Error: %s", err.Error()) 24 | _, _ = fmt.Fprintln(os.Stderr, color.Red.Sprint(msg)) 25 | os.Exit(1) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /release/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/werf/trdl/release 2 | 3 | go 1.23.2 4 | 5 | require ( 6 | github.com/gookit/color v1.5.4 7 | github.com/hashicorp/vault/api v1.16.0 8 | github.com/spf13/cobra v1.9.1 9 | github.com/werf/trdl/client v0.0.0-00010101000000-000000000000 10 | ) 11 | 12 | require ( 13 | github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect 14 | golang.org/x/sys v0.29.0 // indirect 15 | ) 16 | 17 | require ( 18 | github.com/cenkalti/backoff/v4 v4.3.0 19 | github.com/go-jose/go-jose/v4 v4.0.1 // indirect 20 | github.com/hashicorp/errwrap v1.1.0 // indirect 21 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 22 | github.com/hashicorp/go-multierror v1.1.1 // indirect 23 | github.com/hashicorp/go-retryablehttp v0.7.7 // indirect 24 | github.com/hashicorp/go-rootcerts v1.0.2 // indirect 25 | github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect 26 | github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect 27 | github.com/hashicorp/go-sockaddr v1.0.2 // indirect 28 | github.com/hashicorp/hcl v1.0.0 // indirect 29 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 30 | github.com/mitchellh/go-homedir v1.1.0 // indirect 31 | github.com/mitchellh/mapstructure v1.5.0 // indirect 32 | github.com/ryanuber/go-glob v1.0.0 // indirect 33 | github.com/spf13/pflag v1.0.6 // indirect 34 | golang.org/x/crypto v0.32.0 // indirect 35 | golang.org/x/net v0.34.0 // indirect 36 | golang.org/x/sync v0.12.0 37 | golang.org/x/text v0.21.0 // indirect 38 | golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect 39 | ) 40 | 41 | replace github.com/werf/trdl/client => ../client 42 | -------------------------------------------------------------------------------- /release/pkg/client/main.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/werf/trdl/client/pkg/logger" 8 | "github.com/werf/trdl/release/pkg/vault" 9 | ) 10 | 11 | type Interface interface { 12 | Publish(projectName string) error 13 | Release(projectName, gitTag string) error 14 | } 15 | 16 | type Client struct { 17 | client Interface 18 | } 19 | 20 | func newClient(client Interface) *Client { 21 | return &Client{client: client} 22 | } 23 | 24 | func (c *Client) Publish(projectName string) error { 25 | return c.client.Publish(projectName) 26 | } 27 | 28 | func (c *Client) Release(projectName, gitTag string) error { 29 | return c.client.Release(projectName, gitTag) 30 | } 31 | 32 | type NewTrdlVaultClientOpts struct { 33 | Address string 34 | Token string 35 | Retry bool 36 | MaxAttempts int 37 | Delay time.Duration 38 | Logger *logger.Logger 39 | } 40 | 41 | func NewTrdlVaultClient(opts NewTrdlVaultClientOpts) (*Client, error) { 42 | log := opts.Logger 43 | trdlClient, err := vault.NewTrdlClient(vault.NewTrdlClientOpts{ 44 | Address: opts.Address, 45 | Token: opts.Token, 46 | Retry: opts.Retry, 47 | MaxAttempts: opts.MaxAttempts, 48 | Delay: opts.Delay, 49 | Logger: log, 50 | }) 51 | if err != nil { 52 | log.Error(fmt.Sprintf("Unable to create Vault client: %s", err.Error())) 53 | return nil, fmt.Errorf("new Vault client error: %w", err) 54 | } 55 | return newClient(trdlClient), nil 56 | } 57 | -------------------------------------------------------------------------------- /release/pkg/vault/error.go: -------------------------------------------------------------------------------- 1 | package vault 2 | 3 | import "regexp" 4 | 5 | var retriablePatterns = []*regexp.Regexp{ 6 | regexp.MustCompile(`(?i)busy`), 7 | regexp.MustCompile(`(?i)not enough verified PGP signatures`), 8 | } 9 | 10 | func isRetriableError(err error) bool { 11 | if err == nil { 12 | return false 13 | } 14 | msg := err.Error() 15 | for _, pattern := range retriablePatterns { 16 | if pattern.MatchString(msg) { 17 | return true 18 | } 19 | } 20 | return false 21 | } 22 | -------------------------------------------------------------------------------- /release/scripts/verify-dist-binaries.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | script_dir="$(cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" 5 | project_dir="$script_dir/.." 6 | 7 | version="${1:?Version should be set}" 8 | 9 | declare -A regexps 10 | regexps["$project_dir/dist/$version/linux-amd64/bin/trdl-vault"]="x86-64.*statically linked" 11 | regexps["$project_dir/dist/$version/linux-arm64/bin/trdl-vault"]="ARM aarch64.*statically linked" 12 | regexps["$project_dir/dist/$version/darwin-amd64/bin/trdl-vault"]="Mach-O.*x86_64" 13 | regexps["$project_dir/dist/$version/darwin-arm64/bin/trdl-vault"]="Mach-O.*arm64" 14 | regexps["$project_dir/dist/$version/windows-amd64/bin/trdl-vault.exe"]="x86-64.*Windows" 15 | 16 | for filename in "${!regexps[@]}"; do 17 | if ! [[ -f "$filename" ]]; then 18 | echo Binary at "$filename" does not exist. 19 | exit 1 20 | fi 21 | 22 | file "$filename" | awk -v regexp="${regexps[$filename]}" '{print $0; if ($0 ~ regexp) { exit } else { print "Unexpected binary info ^^"; exit 1 }}' 23 | done -------------------------------------------------------------------------------- /release/trdl.yaml: -------------------------------------------------------------------------------- 1 | dockerImage: registry.werf.io/trdl/builder:51b030fe472ec6caa59b068a364bb835ea140588@sha256:f71af3da98446de12e5bb47a789517ba1a17cb19fb17cfc78699552ebcc2c3cf 2 | commands: 3 | - task release:build:dist version={{ .Tag }} 4 | - task release:verify:dist:binaries version={{ .Tag }} 5 | - cp -a release/dist/{{ .Tag }}/* /result 6 | -------------------------------------------------------------------------------- /release/trdl_channels.yaml: -------------------------------------------------------------------------------- 1 | groups: 2 | - name: "0" 3 | channels: 4 | - name: alpha 5 | version: 0.11.0 6 | -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | # Runtime 2 | /vault 3 | /publisher 4 | /.minio_data 5 | /.run -------------------------------------------------------------------------------- /server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest AS builder 2 | 3 | ARG BUILDX_VERSION="v0.10.5" 4 | 5 | RUN apk add --no-cache wget && \ 6 | if [ "$(arch)" = "x86_64" ]; then ARCH="amd64"; \ 7 | elif [ "$(arch)" = "aarch64" ]; then ARCH="arm64"; \ 8 | else echo "Unsupported architecture: $(arch)" && exit 1; fi && \ 9 | wget -O /docker-buildx \ 10 | "https://github.com/docker/buildx/releases/download/${BUILDX_VERSION}/buildx-${BUILDX_VERSION}.linux-${ARCH}" && \ 11 | chmod +x /docker-buildx 12 | 13 | FROM ghcr.io/werf/trdl-dev-vault:latest 14 | RUN addgroup vault ping 15 | ENV VAULT_ADDR=http://localhost:8200 16 | ENV VAULT_TOKEN=root 17 | COPY --from=builder /docker-buildx /usr/lib/docker/cli-plugins/docker-buildx -------------------------------------------------------------------------------- /server/pkg/config/trdl_channels.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | 6 | "gopkg.in/yaml.v2" 7 | ) 8 | 9 | const ( 10 | DefaultTrdlChannelsPath = "trdl_channels.yaml" 11 | ) 12 | 13 | type TrdlChannels struct { 14 | Groups []TrdlGroup `yaml:"groups,omitempty"` 15 | } 16 | 17 | type TrdlGroup struct { 18 | Name string `yaml:"name"` 19 | Channels []TrdlGroupChannel `yaml:"channels,omitempty"` 20 | } 21 | 22 | type TrdlGroupChannel struct { 23 | Name string `yaml:"name"` 24 | Version string `yaml:"version"` 25 | } 26 | 27 | func ParseTrdlChannels(data []byte) (*TrdlChannels, error) { 28 | var res *TrdlChannels 29 | 30 | if err := yaml.Unmarshal(data, &res); err != nil { 31 | return nil, fmt.Errorf("error unmarshalling yaml: %w", err) 32 | } 33 | 34 | return res, nil 35 | } 36 | -------------------------------------------------------------------------------- /server/pkg/docker/secrets.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/werf/trdl/server/pkg/secrets" 9 | ) 10 | 11 | func GetSecretsRunMounts(secrets []secrets.Secret) string { 12 | mounts := buildStringInstruction(secrets) 13 | return fmt.Sprintf("RUN %s", mounts) 14 | } 15 | 16 | func GetSecretsCommandMounts(secrets []secrets.Secret) []string { 17 | args := make([]string, 0, len(secrets)) 18 | for _, s := range secrets { 19 | args = append(args, "--secret", fmt.Sprintf("id=%s", s.Id)) 20 | } 21 | return args 22 | } 23 | 24 | func SetTempEnvVars(secrets []secrets.Secret) error { 25 | for _, s := range secrets { 26 | err := os.Setenv(s.Id, string(s.Data)) 27 | if err != nil { 28 | return fmt.Errorf("unable to use secret data") 29 | } 30 | } 31 | return nil 32 | } 33 | 34 | func buildStringInstruction(secrets []secrets.Secret) string { 35 | var builder strings.Builder 36 | for _, secret := range secrets { 37 | if builder.Len() > 0 { 38 | builder.WriteString(" ") 39 | } 40 | builder.WriteString(fmt.Sprintf("--mount=type=secret,id=%s", secret.Id)) 41 | } 42 | 43 | return builder.String() 44 | } 45 | -------------------------------------------------------------------------------- /server/pkg/docker/util_test.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestValidateImageNameWithDigest_Valid(t *testing.T) { 10 | for _, validName := range []string{ 11 | "repo@sha256:db6697a61d5679b7ca69dbde3dad6be0d17064d5b6b0e9f7be8d456ebb337209", 12 | "repo:tag@sha256:db6697a61d5679b7ca69dbde3dad6be0d17064d5b6b0e9f7be8d456ebb337209", 13 | } { 14 | t.Run(validName, func(t *testing.T) { 15 | err := ValidateImageNameWithDigest(validName) 16 | assert.Nil(t, err) 17 | }) 18 | } 19 | } 20 | 21 | func TestValidateImageNameWithDigest_Invalid(t *testing.T) { 22 | for _, invalidName := range []string{ 23 | "repo", 24 | "repo:tag", 25 | "repo:tag@123", 26 | } { 27 | t.Run(invalidName, func(t *testing.T) { 28 | err := ValidateImageNameWithDigest(invalidName) 29 | assert.Equal(t, err, ErrImageNameWithoutRequiredDigest) 30 | }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /server/pkg/gendocs/generator.go: -------------------------------------------------------------------------------- 1 | package gendocs 2 | 3 | type PagesGenerator interface { 4 | HandlePath(pathPattern string, doc []byte) error 5 | Close() error 6 | HasFormatPathLink() bool 7 | FormatPathLink(pathPattern string) string 8 | } 9 | -------------------------------------------------------------------------------- /server/pkg/gendocs/markdown_pages_generator.go: -------------------------------------------------------------------------------- 1 | package gendocs 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | ) 8 | 9 | type MarkdownPagesGenerator struct { 10 | Dir string 11 | } 12 | 13 | func NewMarkdownPagesGenerator(dir string) *MarkdownPagesGenerator { 14 | return &MarkdownPagesGenerator{ 15 | Dir: dir, 16 | } 17 | } 18 | 19 | func (w *MarkdownPagesGenerator) HasFormatPathLink() bool { 20 | return false 21 | } 22 | 23 | func (w *MarkdownPagesGenerator) FormatPathLink(_ string) string { 24 | return "" 25 | } 26 | 27 | func (w *MarkdownPagesGenerator) HandlePath(pathPattern string, doc []byte) error { 28 | fsPath, err := FormatPathPatternAsFilesystemMarkdownPath(pathPattern) 29 | if err != nil { 30 | return err 31 | } 32 | 33 | f := filepath.Join(w.Dir, fsPath) 34 | if err := os.MkdirAll(filepath.Dir(f), os.ModePerm); err != nil { 35 | return fmt.Errorf("unable to make dir %q: %w", filepath.Dir(f), err) 36 | } 37 | if err := os.WriteFile(f, append(doc, '\n'), 0o644); err != nil { 38 | return fmt.Errorf("unable to write file %q: %w", f, err) 39 | } 40 | 41 | return nil 42 | } 43 | 44 | func (w *MarkdownPagesGenerator) Close() error { 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /server/pkg/git/signatures_loader_test.go: -------------------------------------------------------------------------------- 1 | package git 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo/v2" 5 | . "github.com/onsi/gomega" 6 | ) 7 | 8 | // TODO: refactor this test 9 | var _ = XDescribe("Signatures loader from git NOTES", func() { 10 | It("successfully loads werf signatures", func() { 11 | tagName := "v1.2.84+fix1" 12 | 13 | repo, err := CloneInMemory("https://github.com/werf/werf.git", CloneOptions{TagName: tagName}) 14 | Expect(err).To(Succeed()) 15 | 16 | tref, err := repo.Tag(tagName) 17 | Expect(err).To(Succeed()) 18 | 19 | tobj, err := repo.TagObject(tref.Hash()) 20 | Expect(err).To(Succeed()) 21 | 22 | sigs, err := objectSignaturesFromNotes(repo, tobj.Hash.String()) 23 | Expect(err).To(Succeed()) 24 | 25 | Expect(len(sigs) > 0).To(BeTrue()) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /server/pkg/git/suite_test.go: -------------------------------------------------------------------------------- 1 | package git 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "testing" 7 | 8 | . "github.com/onsi/ginkgo/v2" 9 | . "github.com/onsi/gomega" 10 | 11 | "github.com/werf/trdl/server/pkg/testutil" 12 | ) 13 | 14 | func Test(t *testing.T) { 15 | testutil.MeetsRequirementTools([]string{"git", "git-signatures", "gpg"}) 16 | RegisterFailHandler(Fail) 17 | RunSpecs(t, "Git Suite") 18 | } 19 | 20 | var ( 21 | tmpDir string 22 | testDir string 23 | ) 24 | 25 | var _ = SynchronizedBeforeSuite(func() []byte { 26 | testutil.RunSucceedCommand( 27 | testutil.FixturePath("pgp_keys"), // TODO: move to testutil 28 | "gpg", 29 | "--import", 30 | "developer_private.pgp", 31 | "tl_private.pgp", 32 | "pm_private.pgp", 33 | ) 34 | 35 | return nil 36 | }, func(_ []byte) {}) 37 | 38 | var _ = BeforeEach(func() { 39 | tmpDir = testutil.GetTempDir() 40 | testDir = filepath.Join(tmpDir, "project") 41 | Ω(os.Mkdir(testDir, os.ModePerm)) 42 | }) 43 | 44 | var _ = AfterEach(func() { 45 | err := os.RemoveAll(tmpDir) 46 | Ω(err).ShouldNot(HaveOccurred()) 47 | }) 48 | -------------------------------------------------------------------------------- /server/pkg/git/utils.go: -------------------------------------------------------------------------------- 1 | package git 2 | 3 | import "path/filepath" 4 | 5 | func objectFanoutPaths(objectID string) (res []string) { 6 | res = append(res, objectID) 7 | if len(objectID) <= 2 { 8 | return 9 | } 10 | 11 | for _, path := range objectFanoutPaths(objectID[2:]) { 12 | res = append(res, filepath.Join(objectID[0:2], path)) 13 | } 14 | 15 | return 16 | } 17 | -------------------------------------------------------------------------------- /server/pkg/git/utils_test.go: -------------------------------------------------------------------------------- 1 | package git 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo/v2" 5 | . "github.com/onsi/gomega" 6 | ) 7 | 8 | var _ = Describe("Utils", func() { 9 | It("objectFanoutPaths", func() { 10 | path := "44344dae578b8c9f53617f9dffec40b3f2ad91ae" 11 | expectedRes := []string{ 12 | "44344dae578b8c9f53617f9dffec40b3f2ad91ae", 13 | "44/344dae578b8c9f53617f9dffec40b3f2ad91ae", 14 | "44/34/4dae578b8c9f53617f9dffec40b3f2ad91ae", 15 | "44/34/4d/ae578b8c9f53617f9dffec40b3f2ad91ae", 16 | "44/34/4d/ae/578b8c9f53617f9dffec40b3f2ad91ae", 17 | "44/34/4d/ae/57/8b8c9f53617f9dffec40b3f2ad91ae", 18 | "44/34/4d/ae/57/8b/8c9f53617f9dffec40b3f2ad91ae", 19 | "44/34/4d/ae/57/8b/8c/9f53617f9dffec40b3f2ad91ae", 20 | "44/34/4d/ae/57/8b/8c/9f/53617f9dffec40b3f2ad91ae", 21 | "44/34/4d/ae/57/8b/8c/9f/53/617f9dffec40b3f2ad91ae", 22 | "44/34/4d/ae/57/8b/8c/9f/53/61/7f9dffec40b3f2ad91ae", 23 | "44/34/4d/ae/57/8b/8c/9f/53/61/7f/9dffec40b3f2ad91ae", 24 | "44/34/4d/ae/57/8b/8c/9f/53/61/7f/9d/ffec40b3f2ad91ae", 25 | "44/34/4d/ae/57/8b/8c/9f/53/61/7f/9d/ff/ec40b3f2ad91ae", 26 | "44/34/4d/ae/57/8b/8c/9f/53/61/7f/9d/ff/ec/40b3f2ad91ae", 27 | "44/34/4d/ae/57/8b/8c/9f/53/61/7f/9d/ff/ec/40/b3f2ad91ae", 28 | "44/34/4d/ae/57/8b/8c/9f/53/61/7f/9d/ff/ec/40/b3/f2ad91ae", 29 | "44/34/4d/ae/57/8b/8c/9f/53/61/7f/9d/ff/ec/40/b3/f2/ad91ae", 30 | "44/34/4d/ae/57/8b/8c/9f/53/61/7f/9d/ff/ec/40/b3/f2/ad/91ae", 31 | "44/34/4d/ae/57/8b/8c/9f/53/61/7f/9d/ff/ec/40/b3/f2/ad/91/ae", 32 | } 33 | 34 | Expect(objectFanoutPaths(path)).To(Equal(expectedRes)) 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /server/pkg/keyhelper/load_keys.go: -------------------------------------------------------------------------------- 1 | package keyhelper 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | 8 | "github.com/theupdateframework/go-tuf/data" 9 | "github.com/theupdateframework/go-tuf/encrypted" 10 | ) 11 | 12 | type PersistedKeys struct { 13 | Encrypted bool `json:"encrypted"` 14 | Data json.RawMessage `json:"data"` 15 | } 16 | 17 | func LoadKeys(r io.Reader, passphrase []byte) ([]*data.PrivateKey, error) { 18 | pk := &PersistedKeys{} 19 | 20 | if err := json.NewDecoder(r).Decode(pk); err != nil { 21 | return nil, fmt.Errorf("error unmarshalling keys json data: %w", err) 22 | } 23 | 24 | var keys []*data.PrivateKey 25 | 26 | if !pk.Encrypted { 27 | if err := json.Unmarshal(pk.Data, &keys); err != nil { 28 | return nil, fmt.Errorf("error unmarshalling private key json data: %w", err) 29 | } 30 | 31 | return keys, nil 32 | } 33 | 34 | if err := encrypted.Unmarshal(pk.Data, &keys, passphrase); err != nil { 35 | return nil, fmt.Errorf("unable to decrypt data: %w", err) 36 | } 37 | 38 | return keys, nil 39 | } 40 | -------------------------------------------------------------------------------- /server/pkg/pgp/storage.go: -------------------------------------------------------------------------------- 1 | package pgp 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/hashicorp/vault/sdk/logical" 7 | ) 8 | 9 | const ( 10 | storageKeyPrefixTrustedPGPPublicKey = "trusted_pgp_public_key/" 11 | ) 12 | 13 | func GetTrustedPGPPublicKeys(ctx context.Context, storage logical.Storage) ([]string, error) { 14 | list, err := storage.List(ctx, storageKeyPrefixTrustedPGPPublicKey) 15 | if err != nil { 16 | return nil, err 17 | } 18 | 19 | var trustedPGPPublicKeys []string 20 | for _, name := range list { 21 | storageEntryKey := trustedPGPPublicKeyStorageKey(name) 22 | e, err := storage.Get(ctx, storageEntryKey) 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | trustedPGPPublicKeys = append(trustedPGPPublicKeys, string(e.Value)) 28 | } 29 | 30 | return trustedPGPPublicKeys, nil 31 | } 32 | 33 | func trustedPGPPublicKeyStorageKey(name string) string { 34 | return storageKeyPrefixTrustedPGPPublicKey + name 35 | } 36 | -------------------------------------------------------------------------------- /server/pkg/pgp/util.go: -------------------------------------------------------------------------------- 1 | package pgp 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | 8 | "github.com/hashicorp/go-hclog" 9 | "golang.org/x/crypto/openpgp" 10 | ) 11 | 12 | func VerifyPGPSignatures(pgpSignatures []string, signedReaderFunc func() (io.Reader, error), pgpKeys []string, requiredNumberOfVerifiedSignatures int, logger hclog.Logger) ([]string, int, error) { 13 | if requiredNumberOfVerifiedSignatures == 0 { 14 | return pgpKeys, 0, nil 15 | } 16 | 17 | for _, pgpSignature := range pgpSignatures { 18 | i := 0 19 | l := len(pgpKeys) 20 | for i < l { 21 | keyring, err := openpgp.ReadArmoredKeyRing(strings.NewReader(pgpKeys[i])) 22 | if err != nil { 23 | return nil, 0, err 24 | } 25 | 26 | signedReader, err := signedReaderFunc() 27 | if err != nil { 28 | return nil, 0, err 29 | } 30 | 31 | if _, err = openpgp.CheckArmoredDetachedSignature(keyring, signedReader, strings.NewReader(pgpSignature)); err != nil { 32 | if logger != nil { 33 | logger.Debug(fmt.Sprintf("[DEBUG-SIGNATURES] VerifyPGPSignatures -- will skip pgpKey due to error: %s\n>%v<", err, pgpKeys[i])) 34 | } 35 | i++ 36 | continue 37 | } 38 | 39 | requiredNumberOfVerifiedSignatures-- 40 | if requiredNumberOfVerifiedSignatures == 0 { 41 | return pgpKeys, 0, nil 42 | } 43 | 44 | pgpKeys = append(append([]string{}, pgpKeys[:i]...), pgpKeys[i+1:]...) 45 | break 46 | } 47 | } 48 | 49 | return pgpKeys, requiredNumberOfVerifiedSignatures, nil 50 | } 51 | -------------------------------------------------------------------------------- /server/pkg/publisher/filesystem.go: -------------------------------------------------------------------------------- 1 | package publisher 2 | 3 | import ( 4 | "context" 5 | "io" 6 | ) 7 | 8 | type Filesystem interface { 9 | IsFileExist(ctx context.Context, path string) (bool, error) 10 | ReadFile(ctx context.Context, path string, writer io.WriterAt) error 11 | ReadFileStream(ctx context.Context, path string, writer io.Writer) error 12 | ReadFileBytes(ctx context.Context, path string) ([]byte, error) 13 | WriteFileBytes(ctx context.Context, path string, data []byte) error 14 | WriteFileStream(ctx context.Context, path string, reader io.Reader) error 15 | } 16 | -------------------------------------------------------------------------------- /server/pkg/publisher/interface.go: -------------------------------------------------------------------------------- 1 | package publisher 2 | 3 | import ( 4 | "context" 5 | "io" 6 | 7 | "github.com/hashicorp/vault/sdk/logical" 8 | 9 | "github.com/werf/trdl/server/pkg/config" 10 | "github.com/werf/trdl/server/pkg/util" 11 | ) 12 | 13 | type Interface interface { 14 | GetRepository(ctx context.Context, storage logical.Storage, options RepositoryOptions) (RepositoryInterface, error) 15 | RotateRepositoryKeys(ctx context.Context, storage logical.Storage, repository RepositoryInterface, systemClock util.Clock) error 16 | UpdateTimestamps(ctx context.Context, storage logical.Storage, repository RepositoryInterface, systemClock util.Clock) error 17 | StageReleaseTarget(ctx context.Context, repository RepositoryInterface, releaseName, path string, data io.Reader) error 18 | StageChannelsConfig(ctx context.Context, repository RepositoryInterface, trdlChannelsConfig *config.TrdlChannels) error 19 | StageInMemoryFiles(ctx context.Context, repository RepositoryInterface, files []*InMemoryFile) error 20 | GetExistingReleases(ctx context.Context, repository RepositoryInterface) ([]string, error) 21 | } 22 | 23 | type RepositoryInterface interface { 24 | Init() error 25 | SetPrivKeys(privKeys TufRepoPrivKeys) error 26 | GetPrivKeys() TufRepoPrivKeys 27 | GenPrivKeys() error 28 | RotatePrivKeys(ctx context.Context) (bool, TufRepoPrivKeys, error) 29 | UpdateTimestamps(ctx context.Context, systemClock util.Clock) error 30 | StageTarget(ctx context.Context, pathInsideTargets string, data io.Reader) error 31 | CommitStaged(ctx context.Context) error 32 | GetTargets(ctx context.Context) ([]string, error) 33 | } 34 | -------------------------------------------------------------------------------- /server/pkg/publisher/periodic.go: -------------------------------------------------------------------------------- 1 | package publisher 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/hashicorp/vault/sdk/logical" 7 | ) 8 | 9 | func (m *Publisher) PeriodicFunc(_ context.Context, _ *logical.Request) error { 10 | m.mu.Lock() 11 | defer m.mu.Unlock() 12 | 13 | return nil 14 | } 15 | -------------------------------------------------------------------------------- /server/pkg/publisher/suite_test.go: -------------------------------------------------------------------------------- 1 | package publisher 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestPublisher(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Publisher suite") 13 | } 14 | -------------------------------------------------------------------------------- /server/pkg/tasks_manager/config.go: -------------------------------------------------------------------------------- 1 | package tasks_manager 2 | 3 | import "time" 4 | 5 | const storageKeyConfiguration = "tasks_manager_configuration" 6 | 7 | type configuration struct { 8 | TaskTimeout time.Duration `structs:"task_timeout" json:"task_timeout"` 9 | TaskHistoryLimit int `structs:"task_history_limit" json:"task_history_limit"` 10 | } 11 | -------------------------------------------------------------------------------- /server/pkg/tasks_manager/config_storage.go: -------------------------------------------------------------------------------- 1 | package tasks_manager 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/hashicorp/vault/sdk/logical" 7 | ) 8 | 9 | func getConfiguration(ctx context.Context, storage logical.Storage) (*configuration, error) { 10 | raw, err := storage.Get(ctx, storageKeyConfiguration) 11 | if err != nil { 12 | return nil, err 13 | } 14 | if raw == nil { 15 | return nil, nil 16 | } 17 | 18 | config := new(configuration) 19 | if err := raw.DecodeJSON(config); err != nil { 20 | return nil, err 21 | } 22 | 23 | return config, nil 24 | } 25 | 26 | func putConfiguration(ctx context.Context, storage logical.Storage, cfg *configuration) error { 27 | entry, err := logical.StorageEntryJSON(storageKeyConfiguration, cfg) 28 | if err != nil { 29 | return err 30 | } 31 | 32 | if err := storage.Put(ctx, entry); err != nil { 33 | return err 34 | } 35 | 36 | return err 37 | } 38 | -------------------------------------------------------------------------------- /server/pkg/tasks_manager/interface.go: -------------------------------------------------------------------------------- 1 | package tasks_manager 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/hashicorp/vault/sdk/logical" 7 | ) 8 | 9 | type ActionsInterface interface { 10 | // RunTask runs task or returns busy error 11 | RunTask(ctx context.Context, reqStorage logical.Storage, taskFunc func(ctx context.Context, storage logical.Storage) error) (string, error) 12 | 13 | // AddTask adds task to queue 14 | AddTask(ctx context.Context, reqStorage logical.Storage, taskFunc func(ctx context.Context, storage logical.Storage) error) (string, error) 15 | 16 | // AddOptionalTask adds task to queue if empty 17 | AddOptionalTask(ctx context.Context, reqStorage logical.Storage, taskFunc func(ctx context.Context, storage logical.Storage) error) (string, bool, error) 18 | } 19 | -------------------------------------------------------------------------------- /server/pkg/tasks_manager/worker/buffer.go: -------------------------------------------------------------------------------- 1 | package worker 2 | 3 | import ( 4 | "bytes" 5 | "sync" 6 | ) 7 | 8 | // SafeBuffer prevents possible conflicts when shared by goroutines. 9 | // In the current version, locking is implemented for 10 | // 11 | // - Write, which is used by logboek 12 | // 13 | // - Bytes, which can be used to get the log of the running job 14 | type SafeBuffer struct { 15 | *bytes.Buffer 16 | m sync.Mutex 17 | } 18 | 19 | func NewSafeBuffer() *SafeBuffer { 20 | return &SafeBuffer{ 21 | Buffer: bytes.NewBuffer([]byte{}), 22 | m: sync.Mutex{}, 23 | } 24 | } 25 | 26 | func (b *SafeBuffer) Write(p []byte) (n int, err error) { 27 | b.m.Lock() 28 | defer b.m.Unlock() 29 | return b.Buffer.Write(p) 30 | } 31 | 32 | func (b *SafeBuffer) Bytes() []byte { 33 | b.m.Lock() 34 | defer b.m.Unlock() 35 | return b.Buffer.Bytes() 36 | } 37 | -------------------------------------------------------------------------------- /server/pkg/tasks_manager/worker/interface.go: -------------------------------------------------------------------------------- 1 | package worker 2 | 3 | import "context" 4 | 5 | type Interface interface { 6 | Start() 7 | CancelRunningJobByTaskUUID(uuid string) bool 8 | HoldRunningJobByTaskUUID(uuid string, do func(job *Job)) bool 9 | } 10 | 11 | type TaskCallbacksInterface interface { 12 | TaskStartedCallback(ctx context.Context, uuid string) 13 | TaskFailedCallback(ctx context.Context, uuid string, log []byte, err error) 14 | TaskSucceededCallback(ctx context.Context, uuid string, log []byte) 15 | } 16 | -------------------------------------------------------------------------------- /server/pkg/tasks_manager/worker/job.go: -------------------------------------------------------------------------------- 1 | package worker 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/werf/logboek" 7 | ) 8 | 9 | type Job struct { 10 | taskUUID string 11 | action func() error 12 | ctx context.Context 13 | ctxCancelFunc context.CancelFunc 14 | buff *SafeBuffer 15 | } 16 | 17 | type Task struct { 18 | Context context.Context 19 | UUID string 20 | Action func(ctx context.Context) error 21 | } 22 | 23 | func newJob(task *Task) *Job { 24 | buff := NewSafeBuffer() 25 | loggerCtx := logboek.NewContext(task.Context, logboek.DefaultLogger().NewSubLogger(buff, buff)) 26 | jobContext, jobCtxCancelFunc := context.WithCancel(loggerCtx) 27 | 28 | return &Job{ 29 | ctx: jobContext, 30 | ctxCancelFunc: jobCtxCancelFunc, 31 | taskUUID: task.UUID, 32 | action: func() error { return task.Action(jobContext) }, 33 | buff: buff, 34 | } 35 | } 36 | 37 | func (j *Job) Log() []byte { 38 | return j.buff.Bytes() 39 | } 40 | -------------------------------------------------------------------------------- /server/pkg/testutil/command.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "os" 7 | "os/exec" 8 | "strings" 9 | 10 | . "github.com/onsi/ginkgo/v2" 11 | . "github.com/onsi/gomega" 12 | ) 13 | 14 | func RunSucceedCommand(dir, command string, args ...string) { 15 | _, _ = RunCommandWithOptions(dir, command, args, RunCommandOptions{ShouldSucceed: true}) 16 | } 17 | 18 | func SucceedCommandOutputString(dir, command string, args ...string) string { 19 | res, _ := RunCommandWithOptions(dir, command, args, RunCommandOptions{ShouldSucceed: true}) 20 | return string(res) 21 | } 22 | 23 | type RunCommandOptions struct { 24 | ExtraEnv []string 25 | ToStdin string 26 | ShouldSucceed bool 27 | } 28 | 29 | func RunCommandWithOptions(dir, command string, args []string, options RunCommandOptions) ([]byte, error) { 30 | if isTrdlTestBinaryPath(command) { 31 | args = TrdlBinArgs(args...) 32 | } 33 | 34 | cmd := exec.Command(command, args...) 35 | 36 | if dir != "" { 37 | cmd.Dir = dir 38 | } 39 | 40 | if len(options.ExtraEnv) != 0 { 41 | cmd.Env = append(os.Environ(), options.ExtraEnv...) 42 | } 43 | 44 | if options.ToStdin != "" { 45 | var b bytes.Buffer 46 | b.Write([]byte(options.ToStdin)) 47 | cmd.Stdin = &b 48 | } 49 | 50 | res, err := cmd.CombinedOutput() 51 | _, _ = GinkgoWriter.Write(res) 52 | 53 | if options.ShouldSucceed { 54 | errorDesc := fmt.Sprintf("%[2]s %[3]s (dir: %[1]s)", dir, command, strings.Join(args, " ")) 55 | Ω(err).ShouldNot(HaveOccurred(), errorDesc) 56 | } 57 | 58 | return res, err 59 | } 60 | -------------------------------------------------------------------------------- /server/pkg/testutil/file.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | import ( 4 | . "github.com/onsi/gomega" 5 | "github.com/otiai10/copy" 6 | ) 7 | 8 | func CopyIn(sourcePath, destinationPath string) { 9 | Ω(copy.Copy(sourcePath, destinationPath)).Should(Succeed()) 10 | } 11 | -------------------------------------------------------------------------------- /server/pkg/testutil/git.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | import "strings" 4 | 5 | func GetHeadCommit(workTreeDir string) string { 6 | out := SucceedCommandOutputString( 7 | workTreeDir, 8 | "git", 9 | "rev-parse", "HEAD", 10 | ) 11 | 12 | return strings.TrimSpace(out) 13 | } 14 | -------------------------------------------------------------------------------- /server/pkg/testutil/random.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/Masterminds/goutils" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func GetRandomString(n int) string { 11 | str, err := goutils.CryptoRandomAlphaNumeric(n) 12 | Ω(err).ShouldNot(HaveOccurred()) 13 | return strings.ToLower(str) 14 | } 15 | -------------------------------------------------------------------------------- /server/pkg/util/clock.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import "time" 4 | 5 | type Clock interface { 6 | Now() time.Time 7 | Since(time.Time) time.Duration 8 | } 9 | 10 | func NewSystemClock() *SystemClock { 11 | return &SystemClock{} 12 | } 13 | 14 | type SystemClock struct{} 15 | 16 | func (c *SystemClock) Now() time.Time { 17 | return time.Now() 18 | } 19 | 20 | func (c *SystemClock) Since(t time.Time) time.Duration { 21 | return time.Since(t) 22 | } 23 | 24 | func NewFixedClock(nowTime time.Time) *FixedClock { 25 | return &FixedClock{NowTime: nowTime} 26 | } 27 | 28 | type FixedClock struct { 29 | NowTime time.Time 30 | } 31 | 32 | func (c *FixedClock) Now() time.Time { 33 | return c.NowTime 34 | } 35 | 36 | func (c *FixedClock) Since(t time.Time) time.Duration { 37 | return c.NowTime.Sub(t) 38 | } 39 | -------------------------------------------------------------------------------- /server/pkg/util/io.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/djherbis/buffer" 7 | "github.com/djherbis/nio/v3" 8 | ) 9 | 10 | func BufferedPipedWriterProcess(f func(w io.WriteCloser)) io.ReadCloser { 11 | buf := buffer.New(64 * 1024 * 1024) 12 | r, w := nio.Pipe(buf) 13 | go f(w) 14 | return r 15 | } 16 | -------------------------------------------------------------------------------- /server/pkg/util/logical_error.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type LogicalError error 8 | 9 | func NewLogicalError(format string, a ...interface{}) LogicalError { 10 | return LogicalError(fmt.Errorf(format, a...)) 11 | } 12 | -------------------------------------------------------------------------------- /server/pkg/util/request.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "github.com/hashicorp/vault/sdk/framework" 5 | "github.com/hashicorp/vault/sdk/logical" 6 | ) 7 | 8 | func CheckRequiredFields(req *logical.Request, fields *framework.FieldData) *logical.Response { 9 | for fieldName, schema := range fields.Schema { 10 | if schema.Required && req.Get(fieldName) == nil { 11 | return logical.ErrorResponse("Required field %q must be set", fieldName) 12 | } 13 | } 14 | 15 | return nil 16 | } 17 | -------------------------------------------------------------------------------- /server/pkg/util/utils.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | func IsEnvVarTrue(envVarName string) bool { 8 | switch value := os.Getenv(envVarName); value { 9 | case "", "0", "false", "FALSE", "no", "NO": 10 | return false 11 | } 12 | return true 13 | } 14 | -------------------------------------------------------------------------------- /server/scripts/verify-dist-binaries.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | script_dir="$(cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" 5 | project_dir="$script_dir/.." 6 | 7 | version="${1:?Version should be set}" 8 | 9 | declare -A regexps 10 | regexps["$project_dir/dist/$version/linux-amd64/bin/vault-plugin-secrets-trdl"]="x86-64.*statically linked" 11 | regexps["$project_dir/dist/$version/linux-arm64/bin/vault-plugin-secrets-trdl"]="ARM aarch64.*statically linked" 12 | regexps["$project_dir/dist/$version/darwin-amd64/bin/vault-plugin-secrets-trdl"]="Mach-O.*x86_64" 13 | regexps["$project_dir/dist/$version/darwin-arm64/bin/vault-plugin-secrets-trdl"]="Mach-O.*arm64" 14 | regexps["$project_dir/dist/$version/windows-amd64/bin/vault-plugin-secrets-trdl.exe"]="x86-64.*Windows" 15 | 16 | for filename in "${!regexps[@]}"; do 17 | if ! [[ -f "$filename" ]]; then 18 | echo Binary at "$filename" does not exist. 19 | exit 1 20 | fi 21 | 22 | file "$filename" | awk -v regexp="${regexps[$filename]}" '{print $0; if ($0 ~ regexp) { exit } else { print "Unexpected binary info ^^"; exit 1 }}' 23 | done -------------------------------------------------------------------------------- /server/trdl.yaml: -------------------------------------------------------------------------------- 1 | docker_image: registry.werf.io/trdl/builder:51b030fe472ec6caa59b068a364bb835ea140588@sha256:f71af3da98446de12e5bb47a789517ba1a17cb19fb17cfc78699552ebcc2c3cf 2 | commands: 3 | - task server:build:dist version={{ .Tag }} 4 | - task server:verify:dist:binaries version={{ .Tag }} 5 | - cp -a server/dist/{{ .Tag }}/* /result 6 | -------------------------------------------------------------------------------- /server/trdl_channels.yaml: -------------------------------------------------------------------------------- 1 | groups: 2 | - name: "0" 3 | channels: 4 | - name: alpha 5 | version: 0.8.6 6 | - name: stable 7 | version: 0.8.6 8 | -------------------------------------------------------------------------------- /trdl-builder.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=linux/amd64 golang:1.23-bookworm@sha256:3149bc5043fa58cf127fd8db1fdd4e533b6aed5a40d663d4f4ae43d20386665f 2 | 3 | RUN apt-get -y update && \ 4 | apt-get -y install file && \ 5 | curl -sSLO https://github.com/go-task/task/releases/download/v3.33.1/task_linux_amd64.deb && \ 6 | apt-get -y install ./task_linux_amd64.deb && \ 7 | rm -rf ./task_linux_amd64.deb /var/cache/apt/* /var/lib/apt/lists/* /var/log/* 8 | 9 | ADD server /.trdl-deps/server 10 | ADD client /.trdl-deps/client 11 | ADD e2e /.trdl-deps/e2e 12 | ADD docs /.trdl-deps/docs 13 | ADD Taskfile.dist.yaml /.trdl-deps 14 | 15 | RUN cd /.trdl-deps && \ 16 | task build:dist:all version=base && \ 17 | task client:verify:dist:binaries version=base && \ 18 | task server:verify:dist:binaries version=base && \ 19 | rm -rf /.trdl-deps 20 | 21 | RUN git config --global --add safe.directory /git --------------------------------------------------------------------------------