├── backend ├── .gitignore ├── runner │ └── src │ │ ├── test │ │ ├── resources │ │ │ ├── benchmark-script │ │ │ │ ├── sample-bench-dir │ │ │ │ │ └── bench │ │ │ │ └── sample-bench-dir-2 │ │ │ │ │ └── bench │ │ │ └── logback.xml │ │ └── java │ │ │ └── de │ │ │ └── aaaaaaah │ │ │ └── velcom │ │ │ └── runner │ │ │ └── GitPropertiesTest.java │ │ └── main │ │ ├── java │ │ └── de │ │ │ └── aaaaaaah │ │ │ └── velcom │ │ │ └── runner │ │ │ ├── benchmarking │ │ │ └── output │ │ │ │ └── OutputParseException.java │ │ │ ├── states │ │ │ └── Idle.java │ │ │ ├── tmpdirs │ │ │ └── TaskRepoDir.java │ │ │ ├── Delays.java │ │ │ └── RunnerCliSpec.java │ │ └── resources │ │ ├── logback.xml │ │ └── example_config.json ├── backend │ └── src │ │ ├── main │ │ ├── resources │ │ │ ├── db │ │ │ │ └── migration │ │ │ │ │ ├── V10__remove_repo_admins.sql │ │ │ │ │ ├── V11__speed_up_repo_deletion.sql │ │ │ │ │ ├── V7__global_dimensions_part_1.sql │ │ │ │ │ ├── V4__better_indices.sql │ │ │ │ │ ├── V9__latest_runs_view.sql │ │ │ │ │ ├── V12__commit_tracking_flags.sql │ │ │ │ │ └── V8__global_dimensions_part_2.sql │ │ │ ├── META-INF │ │ │ │ └── aop.xml │ │ │ └── banner.txt │ │ └── java │ │ │ └── de │ │ │ └── aaaaaaah │ │ │ └── velcom │ │ │ └── backend │ │ │ ├── package-info.java │ │ │ ├── listener │ │ │ ├── SynchronizeCommitsException.java │ │ │ ├── commits │ │ │ │ └── DbUpdateException.java │ │ │ ├── github │ │ │ │ ├── GithubApiError.java │ │ │ │ ├── GithubCommandState.java │ │ │ │ ├── JsonBodyHandler.java │ │ │ │ └── FinishedGithubCommand.java │ │ │ └── InvalidRemoteUrlException.java │ │ │ ├── restapi │ │ │ ├── authentication │ │ │ │ ├── Admin.java │ │ │ │ └── AdminAuthenticator.java │ │ │ ├── endpoints │ │ │ │ ├── utils │ │ │ │ │ └── ArgumentParseException.java │ │ │ │ ├── TestTokenEndpoint.java │ │ │ │ ├── ListenerEndpoint.java │ │ │ │ └── DimensionsEndpoint.java │ │ │ ├── exception │ │ │ │ ├── InvalidQueryParamsException.java │ │ │ │ ├── TaskAlreadyExistsException.java │ │ │ │ ├── InvalidQueryParamsExceptionMapper.java │ │ │ │ ├── ArgumentParseExceptionMapper.java │ │ │ │ ├── NoSuchRepoExceptionMapper.java │ │ │ │ ├── NoSuchTaskExceptionMapper.java │ │ │ │ ├── TaskAlreadyExistsExceptionMapper.java │ │ │ │ ├── NoSuchCommitExceptionMapper.java │ │ │ │ └── NoSuchDimensionExceptionMapper.java │ │ │ ├── jsonobjects │ │ │ │ ├── JsonDimensionId.java │ │ │ │ ├── JsonBranch.java │ │ │ │ ├── JsonRepo.java │ │ │ │ ├── JsonTask.java │ │ │ │ ├── JsonRunDescription.java │ │ │ │ ├── JsonRun.java │ │ │ │ └── JsonShortRunDescription.java │ │ │ └── logging │ │ │ │ └── MetricsEndpointFilter.java │ │ │ ├── access │ │ │ ├── taskaccess │ │ │ │ ├── exceptions │ │ │ │ │ ├── TaskCreationException.java │ │ │ │ │ └── NoSuchTaskException.java │ │ │ │ └── entities │ │ │ │ │ └── TaskPriority.java │ │ │ ├── repoaccess │ │ │ │ ├── exceptions │ │ │ │ │ ├── NoSuchRepoException.java │ │ │ │ │ └── FailedToAddRepoException.java │ │ │ │ └── entities │ │ │ │ │ └── RemoteUrl.java │ │ │ ├── dimensionaccess │ │ │ │ ├── exceptions │ │ │ │ │ └── NoSuchDimensionException.java │ │ │ │ └── entities │ │ │ │ │ └── Unit.java │ │ │ ├── committaccess │ │ │ │ ├── entities │ │ │ │ │ ├── CommitHash.java │ │ │ │ │ └── FullCommit.java │ │ │ │ └── exceptions │ │ │ │ │ └── NoSuchCommitException.java │ │ │ ├── benchmarkaccess │ │ │ │ ├── entities │ │ │ │ │ ├── MeasurementError.java │ │ │ │ │ ├── RunError.java │ │ │ │ │ ├── RunErrorType.java │ │ │ │ │ └── sources │ │ │ │ │ │ └── TarSource.java │ │ │ │ └── exceptions │ │ │ │ │ └── NoSuchRunException.java │ │ │ └── archiveaccess │ │ │ │ └── exceptions │ │ │ │ ├── TarTransferException.java │ │ │ │ └── TarRetrieveException.java │ │ │ ├── runner │ │ │ ├── single │ │ │ │ ├── state │ │ │ │ │ ├── IdleState.java │ │ │ │ │ ├── TimeoutState.java │ │ │ │ │ └── AwaitAbortRunReply.java │ │ │ │ └── TeleBinaryOutputStream.java │ │ │ ├── IDispatcher.java │ │ │ └── Delays.java │ │ │ ├── storage │ │ │ ├── db │ │ │ │ ├── DBReadAccess.java │ │ │ │ └── DBWriteAccess.java │ │ │ └── repo │ │ │ │ └── exception │ │ │ │ ├── DirectoryAlreadyExistsException.java │ │ │ │ ├── AddRepositoryException.java │ │ │ │ ├── NoSuchRepositoryException.java │ │ │ │ └── RepositoryAcquisitionException.java │ │ │ ├── util │ │ │ ├── CheckedConsumer.java │ │ │ └── CheckedFunction.java │ │ │ ├── data │ │ │ ├── recentruns │ │ │ │ └── SignificantRun.java │ │ │ ├── runcomparison │ │ │ │ └── RunComparison.java │ │ │ └── significance │ │ │ │ └── SignificanceFactors.java │ │ │ └── KnownHostsIgnoringSshdFactory.java │ │ └── test │ │ ├── resources │ │ └── logback.xml │ │ └── java │ │ └── de │ │ └── aaaaaaah │ │ └── velcom │ │ └── backend │ │ ├── restapi │ │ ├── jsonobjects │ │ │ ├── JsonDimensionInfoTest.java │ │ │ ├── JsonTaskTest.java │ │ │ ├── JsonCommitDescriptionTest.java │ │ │ ├── JsonRunDescriptionTest.java │ │ │ ├── SerializingTest.java │ │ │ └── JsonRunTest.java │ │ └── endpoints │ │ │ └── utils │ │ │ └── EndpointUtilsTest.java │ │ └── access │ │ └── dimensionaccess │ │ └── entities │ │ └── DimensionTest.java ├── shared │ └── src │ │ ├── main │ │ └── java │ │ │ └── de │ │ │ └── aaaaaaah │ │ │ └── velcom │ │ │ └── shared │ │ │ ├── protocol │ │ │ ├── serialization │ │ │ │ ├── Status.java │ │ │ │ ├── clientbound │ │ │ │ │ ├── GetStatus.java │ │ │ │ │ ├── ClientBound.java │ │ │ │ │ ├── ClearResult.java │ │ │ │ │ ├── GetResult.java │ │ │ │ │ ├── AbortRun.java │ │ │ │ │ ├── ClientBoundPacket.java │ │ │ │ │ └── ClientBoundPacketType.java │ │ │ │ └── serverbound │ │ │ │ │ ├── ClearResultReply.java │ │ │ │ │ ├── ServerBound.java │ │ │ │ │ ├── RequestRun.java │ │ │ │ │ ├── AbortRunReply.java │ │ │ │ │ └── ServerBoundPacket.java │ │ │ ├── RunnerConnectionHeader.java │ │ │ ├── statemachine │ │ │ │ └── State.java │ │ │ └── RunnerDenyReason.java │ │ │ ├── util │ │ │ ├── OSCheck.java │ │ │ ├── execution │ │ │ │ ├── DaemonThreadFactory.java │ │ │ │ ├── StreamsProcessOutput.java │ │ │ │ └── ProgramResult.java │ │ │ ├── ExceptionHelper.java │ │ │ ├── StringHelper.java │ │ │ ├── LinesWithOffset.java │ │ │ ├── StringOutputStream.java │ │ │ └── Pair.java │ │ │ └── GitProperties.java │ │ └── test │ │ ├── java │ │ └── de │ │ │ └── aaaaaaah │ │ │ └── velcom │ │ │ └── shared │ │ │ ├── protocol │ │ │ └── serialization │ │ │ │ ├── SerializerTest.java │ │ │ │ ├── SerializerBasedTest.java │ │ │ │ ├── clientbound │ │ │ │ ├── AbortRunTest.java │ │ │ │ ├── GetResultTest.java │ │ │ │ ├── GetStatusTest.java │ │ │ │ ├── ClearResultTest.java │ │ │ │ ├── RequestRunReplyTest.java │ │ │ │ └── ClientBoundPacketTest.java │ │ │ │ └── serverbound │ │ │ │ ├── RequestRunTest.java │ │ │ │ ├── AbortRunReplyTest.java │ │ │ │ ├── ClearResultReplyTest.java │ │ │ │ └── ServerBoundPacketTest.java │ │ │ └── util │ │ │ ├── OSCheckTest.java │ │ │ ├── PairTest.java │ │ │ ├── compression │ │ │ └── PermissionsHelperTest.java │ │ │ ├── ExceptionHelperTest.java │ │ │ └── systeminfo │ │ │ └── LinuxSystemInfoTest.java │ │ └── resources │ │ └── logback.xml ├── aggregator │ └── pom.xml └── checkstyle.xml ├── frontend ├── .browserslistrc ├── .prettierignore ├── .prettierrc.json ├── src │ ├── vue-echarts.d.ts │ ├── ansi-to-html.d.ts │ ├── diegraphs-extra.d.ts │ ├── assets │ │ ├── 404.png │ │ └── mini-logo.png │ ├── shims-vue.d.ts │ ├── shims-vuetify.d.ts │ ├── vue-snackbar.d.ts │ ├── util │ │ ├── FlavorTypes.ts │ │ ├── json │ │ │ ├── StatusComparisonJsonHelper.ts │ │ │ ├── CleanupJsonHelper.ts │ │ │ ├── NewJsonHelper.ts │ │ │ ├── RepoJsonHelper.ts │ │ │ ├── QueueJsonHelper.ts │ │ │ └── RunSearchJsonHelper.ts │ │ ├── Arrays.ts │ │ ├── Debouncer.ts │ │ ├── Measurements.ts │ │ ├── Errors.ts │ │ ├── Clipboards.ts │ │ ├── GraphVariantSelection.ts │ │ └── DayEquidistantUtils.ts │ ├── class-components-router-hooks.ts │ ├── shims-tsx.d.ts │ ├── scss │ │ └── variables.scss │ ├── vue-class-components.d.ts │ ├── store │ │ └── modules │ │ │ ├── runSearchStore.ts │ │ │ └── cleanupStore.ts │ ├── components │ │ ├── misc │ │ │ ├── InlineMinimalRepoDisplay.vue │ │ │ ├── TextChip.vue │ │ │ └── ThemeSelector.vue │ │ ├── rundetail │ │ │ └── MeasurementValueDisplay.vue │ │ └── queue │ │ │ └── WorkerOverview.vue │ ├── views │ │ └── Settings.vue │ └── plugins │ │ └── vuetify.ts ├── .env.development ├── .git-blame-ignore-revs ├── babel.config.js ├── .env.development-remote ├── public │ ├── v-icon.png │ ├── fonts │ │ ├── roboto-v20-latin_latin-ext-100.eot │ │ ├── roboto-v20-latin_latin-ext-100.ttf │ │ ├── roboto-v20-latin_latin-ext-100.woff │ │ ├── roboto-v20-latin_latin-ext-300.eot │ │ ├── roboto-v20-latin_latin-ext-300.ttf │ │ ├── roboto-v20-latin_latin-ext-300.woff │ │ ├── roboto-v20-latin_latin-ext-500.eot │ │ ├── roboto-v20-latin_latin-ext-500.ttf │ │ ├── roboto-v20-latin_latin-ext-500.woff │ │ ├── roboto-v20-latin_latin-ext-700.eot │ │ ├── roboto-v20-latin_latin-ext-700.ttf │ │ ├── roboto-v20-latin_latin-ext-700.woff │ │ ├── roboto-v20-latin_latin-ext-900.eot │ │ ├── roboto-v20-latin_latin-ext-900.ttf │ │ ├── roboto-v20-latin_latin-ext-900.woff │ │ ├── roboto-v20-latin_latin-ext-100.woff2 │ │ ├── roboto-v20-latin_latin-ext-300.woff2 │ │ ├── roboto-v20-latin_latin-ext-500.woff2 │ │ ├── roboto-v20-latin_latin-ext-700.woff2 │ │ ├── roboto-v20-latin_latin-ext-900.woff2 │ │ ├── roboto-v20-latin_latin-ext-italic.eot │ │ ├── roboto-v20-latin_latin-ext-italic.ttf │ │ ├── roboto-v20-latin_latin-ext-italic.woff │ │ ├── roboto-v20-latin_latin-ext-regular.eot │ │ ├── roboto-v20-latin_latin-ext-regular.ttf │ │ ├── roboto-v20-latin_latin-ext-100italic.eot │ │ ├── roboto-v20-latin_latin-ext-100italic.ttf │ │ ├── roboto-v20-latin_latin-ext-300italic.eot │ │ ├── roboto-v20-latin_latin-ext-300italic.ttf │ │ ├── roboto-v20-latin_latin-ext-500italic.eot │ │ ├── roboto-v20-latin_latin-ext-500italic.ttf │ │ ├── roboto-v20-latin_latin-ext-700italic.eot │ │ ├── roboto-v20-latin_latin-ext-700italic.ttf │ │ ├── roboto-v20-latin_latin-ext-900italic.eot │ │ ├── roboto-v20-latin_latin-ext-900italic.ttf │ │ ├── roboto-v20-latin_latin-ext-italic.woff2 │ │ ├── roboto-v20-latin_latin-ext-regular.woff │ │ ├── roboto-v20-latin_latin-ext-regular.woff2 │ │ ├── roboto-v20-latin_latin-ext-100italic.woff │ │ ├── roboto-v20-latin_latin-ext-100italic.woff2 │ │ ├── roboto-v20-latin_latin-ext-300italic.woff │ │ ├── roboto-v20-latin_latin-ext-300italic.woff2 │ │ ├── roboto-v20-latin_latin-ext-500italic.woff │ │ ├── roboto-v20-latin_latin-ext-500italic.woff2 │ │ ├── roboto-v20-latin_latin-ext-700italic.woff │ │ ├── roboto-v20-latin_latin-ext-700italic.woff2 │ │ ├── roboto-v20-latin_latin-ext-900italic.woff │ │ └── roboto-v20-latin_latin-ext-900italic.woff2 │ └── index.html ├── .env.production ├── .gitignore ├── README.md ├── vue.config.js ├── tsconfig.json └── package.json ├── cli ├── .gitignore ├── velcom_cli │ ├── commands │ │ ├── __init__.py │ │ ├── default_config.py │ │ ├── list_repos.py │ │ └── print_config.py │ └── __init__.py ├── pyproject.toml ├── setup.py └── setup.cfg ├── .vscode ├── extensions.json └── settings.json ├── scripts ├── fmt ├── fmt-backend ├── fmtchk-backend ├── fmtchk ├── fmt-frontend ├── fmtchk-frontend ├── clean-backend ├── clean-frontend ├── build-backend ├── build-frontend └── docker │ └── start-backend-docker.sh ├── .gitignore ├── .idea ├── codeStyles │ ├── codeStyleConfig.xml │ └── Project.xml ├── google-java-format.xml └── prettier.xml ├── docs └── install.md ├── LICENSE └── README.md /backend/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /frontend/.browserslistrc: -------------------------------------------------------------------------------- 1 | defaults 2 | -------------------------------------------------------------------------------- /frontend/.prettierignore: -------------------------------------------------------------------------------- 1 | /dist/ 2 | -------------------------------------------------------------------------------- /cli/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | .venv/ 3 | *.egg-info/ 4 | -------------------------------------------------------------------------------- /frontend/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100 3 | } 4 | -------------------------------------------------------------------------------- /frontend/src/vue-echarts.d.ts: -------------------------------------------------------------------------------- 1 | declare module "vue-echarts"; 2 | -------------------------------------------------------------------------------- /frontend/src/ansi-to-html.d.ts: -------------------------------------------------------------------------------- 1 | declare module "ansi-to-html"; 2 | -------------------------------------------------------------------------------- /frontend/.env.development: -------------------------------------------------------------------------------- 1 | VUE_APP_BASE_URL="http://localhost:9001" 2 | -------------------------------------------------------------------------------- /backend/runner/src/test/resources/benchmark-script/sample-bench-dir/bench: -------------------------------------------------------------------------------- 1 | hello world!! -------------------------------------------------------------------------------- /frontend/src/diegraphs-extra.d.ts: -------------------------------------------------------------------------------- 1 | declare module "dygraphs/src/extras/crosshair.js"; 2 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { "recommendations": ["Vue.volar", "esbenp.prettier-vscode"] } 2 | -------------------------------------------------------------------------------- /backend/runner/src/test/resources/benchmark-script/sample-bench-dir-2/bench: -------------------------------------------------------------------------------- 1 | hello world IN SECOND! -------------------------------------------------------------------------------- /backend/backend/src/main/resources/db/migration/V10__remove_repo_admins.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE repo_token; 2 | -------------------------------------------------------------------------------- /frontend/.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # Reformat all files 2 | 1786021249c430f497bcf945c87044fecb522d9a 3 | -------------------------------------------------------------------------------- /frontend/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ["@vue/cli-plugin-babel/preset"], 3 | }; 4 | -------------------------------------------------------------------------------- /cli/velcom_cli/commands/__init__.py: -------------------------------------------------------------------------------- 1 | from . import bench_dir, default_config, list_repos, print_config 2 | -------------------------------------------------------------------------------- /frontend/.env.development-remote: -------------------------------------------------------------------------------- 1 | VUE_APP_BASE_URL="http://speedcenter.informatik.kit.edu/mathlib4/api" 2 | -------------------------------------------------------------------------------- /frontend/public/v-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/v-icon.png -------------------------------------------------------------------------------- /frontend/src/assets/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/src/assets/404.png -------------------------------------------------------------------------------- /frontend/.env.production: -------------------------------------------------------------------------------- 1 | NODE_ENV="production" 2 | BASE_URL="." 3 | VUE_APP_LOCATION_INDEPENDENT_IMAGE="true" 4 | -------------------------------------------------------------------------------- /frontend/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.vue" { 2 | import Vue from "vue"; 3 | export default Vue; 4 | } 5 | -------------------------------------------------------------------------------- /scripts/fmt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | ./scripts/fmt-frontend 5 | ./scripts/fmt-backend 6 | -------------------------------------------------------------------------------- /cli/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | -------------------------------------------------------------------------------- /frontend/src/assets/mini-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/src/assets/mini-logo.png -------------------------------------------------------------------------------- /scripts/fmt-backend: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | pushd backend 5 | mvn spotless:apply 6 | popd 7 | -------------------------------------------------------------------------------- /scripts/fmtchk-backend: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | pushd backend 5 | mvn spotless:check 6 | popd 7 | -------------------------------------------------------------------------------- /scripts/fmtchk: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | ./scripts/fmtchk-frontend 5 | ./scripts/fmtchk-backend 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/* 2 | !/.idea/codeStyles/ 3 | !/.idea/codeStyles/* 4 | !/.idea/google-java-format.xml 5 | !/.idea/prettier.xml 6 | -------------------------------------------------------------------------------- /frontend/src/shims-vuetify.d.ts: -------------------------------------------------------------------------------- 1 | declare module "vuetify/lib/framework" { 2 | import Vuetify from "vuetify"; 3 | export default Vuetify; 4 | } 5 | -------------------------------------------------------------------------------- /scripts/fmt-frontend: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | pushd frontend 5 | yarnpkg install --ignore-engines 6 | yarnpkg run fmt 7 | popd 8 | -------------------------------------------------------------------------------- /scripts/fmtchk-frontend: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | pushd frontend 5 | yarnpkg install --ignore-engines 6 | yarnpkg run fmtchk 7 | popd 8 | -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-100.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-100.eot -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-100.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-100.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-100.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-100.woff -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-300.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-300.eot -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-300.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-300.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-300.woff -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-500.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-500.eot -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-500.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-500.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-500.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-500.woff -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-700.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-700.eot -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-700.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-700.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-700.woff -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-900.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-900.eot -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-900.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-900.woff -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-100.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-100.woff2 -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-300.woff2 -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-500.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-500.woff2 -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-700.woff2 -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-900.woff2 -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-italic.eot -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-italic.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-italic.woff -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-regular.eot -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-regular.ttf -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[jsonc]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, 3 | "[vue]": { "editor.defaultFormatter": "esbenp.prettier-vscode" } 4 | } 5 | -------------------------------------------------------------------------------- /backend/backend/src/main/resources/META-INF/aop.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-100italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-100italic.eot -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-100italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-100italic.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-300italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-300italic.eot -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-300italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-300italic.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-500italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-500italic.eot -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-500italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-500italic.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-700italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-700italic.eot -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-700italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-700italic.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-900italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-900italic.eot -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-900italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-900italic.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-italic.woff2 -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-regular.woff -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-regular.woff2 -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-100italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-100italic.woff -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-100italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-100italic.woff2 -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-300italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-300italic.woff -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-300italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-300italic.woff2 -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-500italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-500italic.woff -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-500italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-500italic.woff2 -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-700italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-700italic.woff -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-700italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-700italic.woff2 -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-900italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-900italic.woff -------------------------------------------------------------------------------- /frontend/public/fonts/roboto-v20-latin_latin-ext-900italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanprover/velcom/HEAD/frontend/public/fonts/roboto-v20-latin_latin-ext-900italic.woff2 -------------------------------------------------------------------------------- /frontend/src/vue-snackbar.d.ts: -------------------------------------------------------------------------------- 1 | import { ISnackbar } from "./util/Snackbar"; 2 | 3 | declare module "vue/types/vue" { 4 | interface Vue { 5 | $globalSnackbar: ISnackbar; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /cli/setup.py: -------------------------------------------------------------------------------- 1 | # Required for `pip install --editable`. 2 | # See https://setuptools.readthedocs.io/en/latest/userguide/quickstart.html#development-mode 3 | 4 | import setuptools 5 | setuptools.setup() 6 | -------------------------------------------------------------------------------- /.idea/google-java-format.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /docs/install.md: -------------------------------------------------------------------------------- 1 | # Installation overview 2 | 3 | All the possible ways to install VelCom on your system. 4 | 5 | - [Manual installation](install_manual.md) 6 | - [Installation and deployment with Docker](install_docker.md) 7 | -------------------------------------------------------------------------------- /backend/backend/src/main/resources/db/migration/V11__speed_up_repo_deletion.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX idx_commit_relationship_parent 2 | ON commit_relationship(repo_id, parent_hash); 3 | 4 | CREATE INDEX idx_measurement_rid 5 | ON measurement(run_id); 6 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/package-info.java: -------------------------------------------------------------------------------- 1 | /** The root backend package. */ 2 | @ParametersAreNonnullByDefault 3 | package de.aaaaaaah.velcom.backend; 4 | 5 | import javax.annotation.ParametersAreNonnullByDefault; 6 | -------------------------------------------------------------------------------- /cli/setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = velcom_cli 3 | version = 0.0.1 4 | 5 | [options] 6 | packages = find: 7 | install-requires = 8 | requests >= 2.25.1 9 | 10 | [options.entry_points] 11 | console_scripts = 12 | velcom = velcom_cli:main 13 | -------------------------------------------------------------------------------- /frontend/src/util/FlavorTypes.ts: -------------------------------------------------------------------------------- 1 | // https://spin.atomicobject.com/2018/01/15/typescript-flexible-nominal-typing/#comment-604580 2 | interface Flavoring { 3 | _type?: FlavorT; 4 | } 5 | export type Flavor = T & Flavoring; 6 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/listener/SynchronizeCommitsException.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.listener; 2 | 3 | public class SynchronizeCommitsException extends Exception { 4 | 5 | public SynchronizeCommitsException() {} 6 | } 7 | -------------------------------------------------------------------------------- /frontend/src/class-components-router-hooks.ts: -------------------------------------------------------------------------------- 1 | import Component from "vue-class-component"; 2 | 3 | // Register the router hooks with their names 4 | Component.registerHooks([ 5 | "beforeRouteEnter", 6 | "beforeRouteLeave", 7 | "beforeRouteUpdate", // for vue-router 2.2+ 8 | ]); 9 | -------------------------------------------------------------------------------- /.idea/prettier.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /backend/backend/src/main/resources/db/migration/V7__global_dimensions_part_1.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE dimension ( 2 | benchmark TEXT NOT NULL, 3 | metric TEXT NOT NULL, 4 | unit TEXT NOT NULL, 5 | interpretation TEXT NOT NULL, 6 | significant BOOLEAN NOT NULL, 7 | 8 | PRIMARY KEY (benchmark, metric) 9 | ); 10 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/restapi/authentication/Admin.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.authentication; 2 | 3 | import java.security.Principal; 4 | 5 | public class Admin implements Principal { 6 | 7 | public Admin() {} 8 | 9 | @Override 10 | public String getName() { 11 | return "admin"; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from "vue"; 2 | 3 | declare global { 4 | namespace JSX { 5 | // tslint:disable no-empty-interface 6 | interface Element extends VNode {} 7 | // tslint:disable no-empty-interface 8 | interface ElementClass extends Vue {} 9 | interface IntrinsicElements { 10 | [elem: string]: any; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/restapi/endpoints/utils/ArgumentParseException.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.endpoints.utils; 2 | 3 | /** A query parameter could not be parsed correctly. */ 4 | public class ArgumentParseException extends RuntimeException { 5 | 6 | public ArgumentParseException(String message) { 7 | super(message); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /frontend/src/scss/variables.scss: -------------------------------------------------------------------------------- 1 | @import "~vuetify/src/styles/styles.sass"; 2 | 3 | $material-dark: ( 4 | "background": #2f3136, 5 | "cards": #2f3136, 6 | "text": ( 7 | "primary": rgba(255, 255, 255, 0.87), 8 | ), 9 | ); 10 | 11 | // You need to map-merge your new SASS variables 12 | $grid-breakpoints: map-merge( 13 | $grid-breakpoints, 14 | ( 15 | xl: 1300px, 16 | ) 17 | ); 18 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/restapi/exception/InvalidQueryParamsException.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.exception; 2 | 3 | /** An exception thrown when an endpoint's query parameters are invalid. */ 4 | public class InvalidQueryParamsException extends RuntimeException { 5 | 6 | public InvalidQueryParamsException(String message) { 7 | super(message); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/listener/commits/DbUpdateException.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.listener.commits; 2 | 3 | /** An exception thrown when the {@link DbUpdater} failed to update the database for a repo. */ 4 | public class DbUpdateException extends Exception { 5 | 6 | public DbUpdateException(String message, Throwable cause) { 7 | super(message, cause); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /scripts/clean-backend: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import argparse 4 | import shlex 5 | import subprocess 6 | from pathlib import Path 7 | 8 | DIR: Path = Path("backend") 9 | 10 | 11 | def run(*cmd: str | Path) -> None: 12 | print(f"$ {shlex.join(str(arg) for arg in cmd)}") 13 | subprocess.run(cmd, check=True, cwd=DIR) 14 | 15 | 16 | parser = argparse.ArgumentParser() 17 | _args = parser.parse_args() 18 | 19 | run("mvn", "clean") 20 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # frontend 2 | 3 | ## Project setup 4 | 5 | ``` 6 | yarn install 7 | ``` 8 | 9 | ### Compiles and hot-reloads for development 10 | 11 | ``` 12 | yarn serve 13 | ``` 14 | 15 | ### Compiles and minifies for production 16 | 17 | ``` 18 | yarn build 19 | ``` 20 | 21 | ### Lints and fixes files 22 | 23 | ``` 24 | yarn lint 25 | ``` 26 | 27 | ### Customize configuration 28 | 29 | See [Configuration Reference](https://cli.vuejs.org/config/). 30 | -------------------------------------------------------------------------------- /cli/velcom_cli/commands/default_config.py: -------------------------------------------------------------------------------- 1 | import io 2 | 3 | 4 | def register(subparsers): 5 | parser = subparsers.add_parser( 6 | "default-config", 7 | aliases=["dc"], 8 | help="print the default config file on stdout", 9 | ) 10 | parser.set_defaults(f=command) 11 | 12 | 13 | def command(config): 14 | with io.StringIO() as stream: 15 | config.default_parser().write(stream) 16 | print(stream.getvalue(), end="") 17 | -------------------------------------------------------------------------------- /backend/backend/src/main/resources/db/migration/V4__better_indices.sql: -------------------------------------------------------------------------------- 1 | -- Unnecessary old indices 2 | DROP INDEX idx_measurement_id_rid; 3 | DROP INDEX idx_measurement_value_id_rid; 4 | 5 | -- This index speeds up some queries by two to three orders of magnitude... 6 | CREATE INDEX idx_measurement_value_mid ON measurement_value(measurement_id); 7 | -- This one only by one order of magnitude, but hey, that's not bad either. 8 | CREATE INDEX idx_run_rid_ch ON run(repo_id, commit_hash); 9 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/protocol/serialization/Status.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.serialization; 2 | 3 | /** This enum describes a runner's state from the view of a backend it is connected to. */ 4 | public enum Status { 5 | /** The runner is running a benchmark. */ 6 | RUN, 7 | 8 | /** The runner is aborting a benchmark. */ 9 | ABORT, 10 | 11 | /** The runner is not running a benchmark. */ 12 | IDLE 13 | } 14 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/util/OSCheck.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.util; 2 | 3 | /** Check whether this program is running on a lame OS. */ 4 | public final class OSCheck { 5 | 6 | private OSCheck() { 7 | throw new UnsupportedOperationException("No instantiation"); 8 | } 9 | 10 | public static boolean isStupidWindows() { 11 | return System.getProperty("os.name", "generic").toLowerCase().contains("windows"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/listener/github/GithubApiError.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.listener.github; 2 | 3 | import java.net.URI; 4 | 5 | public class GithubApiError extends Exception { 6 | 7 | private final URI url; 8 | 9 | public GithubApiError(String message, URI url) { 10 | super("Error while accessing " + url + ": " + message); 11 | this.url = url; 12 | } 13 | 14 | public URI getUrl() { 15 | return url; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /backend/shared/src/test/java/de/aaaaaaah/velcom/shared/protocol/serialization/SerializerTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.serialization; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | public class SerializerTest extends SerializerBasedTest { 8 | 9 | @Test 10 | void deserializeFailReturnsEmpty() { 11 | assertThat(serializer.deserialize("hello", SerializerBasedTest.class)).isEmpty(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/runner/src/main/java/de/aaaaaaah/velcom/runner/benchmarking/output/OutputParseException.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.runner.benchmarking.output; 2 | 3 | /** An exception detailing an error parsing the script output. */ 4 | public class OutputParseException extends RuntimeException { 5 | 6 | public OutputParseException(String message) { 7 | super(message); 8 | } 9 | 10 | public OutputParseException(String message, Throwable cause) { 11 | super(message, cause); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/util/json/StatusComparisonJsonHelper.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ 2 | import { StatusComparisonPoint } from "@/store/types"; 3 | import { runFromJson } from "@/util/json/CommitComparisonJsonHelper"; 4 | 5 | export function statusComparisonPointFromJson(json: any): StatusComparisonPoint { 6 | return new StatusComparisonPoint( 7 | json.repo_id, 8 | json.run ? runFromJson(json.run) : undefined, 9 | json.commit_hash, 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/access/taskaccess/exceptions/TaskCreationException.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.access.taskaccess.exceptions; 2 | 3 | public class TaskCreationException extends Exception { 4 | 5 | private final boolean ourFault; 6 | 7 | public TaskCreationException(String message, boolean ourFault) { 8 | super(message); 9 | 10 | this.ourFault = ourFault; 11 | } 12 | 13 | public boolean isOurFault() { 14 | return ourFault; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /frontend/src/util/json/CleanupJsonHelper.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ 2 | import { CleanupDimension } from "@/store/types"; 3 | import { dimensionFromJson } from "@/util/json/RepoJsonHelper"; 4 | 5 | export function cleanupDimensionFromJson(cleanupDimension: any) { 6 | return new CleanupDimension( 7 | dimensionFromJson(cleanupDimension.dimension), 8 | cleanupDimension.runs, 9 | cleanupDimension.untracked_runs, 10 | cleanupDimension.unreachable_runs, 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /backend/backend/src/test/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/util/execution/DaemonThreadFactory.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.util.execution; 2 | 3 | import java.util.concurrent.ThreadFactory; 4 | import javax.annotation.Nonnull; 5 | 6 | /** A {@link ThreadFactory} that returns daemon threads. */ 7 | public class DaemonThreadFactory implements ThreadFactory { 8 | 9 | @Override 10 | public Thread newThread(@Nonnull Runnable r) { 11 | Thread thread = new Thread(r); 12 | thread.setDaemon(true); 13 | return thread; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /backend/shared/src/test/java/de/aaaaaaah/velcom/shared/util/OSCheckTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.util; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class OSCheckTest { 8 | 9 | @Test 10 | void returnsCorrectValue() { 11 | if (System.getProperty("os.name").toLowerCase().contains("windows")) { 12 | assertThat(OSCheck.isStupidWindows()).isTrue(); 13 | } else { 14 | assertThat(OSCheck.isStupidWindows()).isFalse(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /backend/shared/src/test/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /backend/runner/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /backend/runner/src/test/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /frontend/vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | transpileDependencies: ["vue-echarts", "resize-detector"], 3 | publicPath: process.env.BASE_URL, 4 | chainWebpack: (config) => { 5 | config.plugin("define").tap((definitions) => { 6 | // get git info from command line 7 | // eslint-disable-next-line 8 | const commitHash = require("child_process").execSync("git rev-parse HEAD").toString(); 9 | definitions[0]["process.env"]["__COMMIT_HASH__"] = JSON.stringify(commitHash); 10 | return definitions; 11 | }); 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/protocol/serialization/clientbound/GetStatus.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.serialization.clientbound; 2 | 3 | import de.aaaaaaah.velcom.shared.protocol.serialization.Serializer; 4 | 5 | /** A command requesting the runner's current status. */ 6 | public class GetStatus implements ClientBound { 7 | 8 | @Override 9 | public ClientBoundPacket asPacket(Serializer serializer) { 10 | return new ClientBoundPacket(ClientBoundPacketType.GET_STATUS, serializer.serializeTree(this)); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /scripts/clean-frontend: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import argparse 4 | import shutil 5 | from pathlib import Path 6 | 7 | DIR: Path = Path("frontend") 8 | 9 | 10 | def rm(path: Path) -> None: 11 | print(f"Removing {path}") 12 | shutil.rmtree(path, ignore_errors=True) 13 | 14 | 15 | parser = argparse.ArgumentParser() 16 | parser.add_argument( 17 | "--all", 18 | "-a", 19 | action="store_true", 20 | help="remove node_modules as well", 21 | ) 22 | args = parser.parse_args() 23 | 24 | rm(DIR / "dist") 25 | 26 | if args.all: 27 | rm(DIR / "node_modules") 28 | -------------------------------------------------------------------------------- /cli/velcom_cli/commands/list_repos.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | import requests 4 | 5 | 6 | def register(subparsers): 7 | parser = subparsers.add_parser( 8 | "list-repos", 9 | aliases=["lr"], 10 | help="list all existing repos and their ids", 11 | ) 12 | parser.set_defaults(f=command) 13 | 14 | 15 | def command(config): 16 | api_url = config.geturl("api_url") 17 | 18 | response = requests.get(api_url + "all-repos") 19 | repos = json.loads(response.text)["repos"] 20 | for repo in repos: 21 | print(f"{repo['id']} - {repo['name']}") 22 | -------------------------------------------------------------------------------- /scripts/build-backend: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import argparse 4 | import shlex 5 | import subprocess 6 | from pathlib import Path 7 | 8 | DIR: Path = Path("backend") 9 | 10 | 11 | def run(*cmd: str | Path) -> None: 12 | print(f"$ {shlex.join(str(arg) for arg in cmd)}") 13 | subprocess.run(cmd, check=True, cwd=DIR) 14 | 15 | 16 | parser = argparse.ArgumentParser() 17 | _args = parser.parse_args() 18 | 19 | run("mvn", "package") 20 | print() 21 | print("The backend.jar is at backend/backend/target/backend.jar") 22 | print("The runner.jar is at backend/runner/target/runner.jar") 23 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/runner/single/state/IdleState.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.runner.single.state; 2 | 3 | import de.aaaaaaah.velcom.backend.runner.single.RunnerConnection; 4 | import de.aaaaaaah.velcom.backend.runner.single.TeleRunner; 5 | 6 | /** The tele-runner is in an idle state. */ 7 | public class IdleState extends TeleRunnerState { 8 | 9 | public IdleState(TeleRunner runner, RunnerConnection connection) { 10 | super(runner, connection); 11 | } 12 | 13 | @Override 14 | public boolean isResting() { 15 | return true; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/protocol/serialization/serverbound/ClearResultReply.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.serialization.serverbound; 2 | 3 | import de.aaaaaaah.velcom.shared.protocol.serialization.Serializer; 4 | 5 | /** Reply indicating the current result was cleared. */ 6 | public class ClearResultReply implements ServerBound { 7 | 8 | @Override 9 | public ServerBoundPacket asPacket(Serializer serializer) { 10 | return new ServerBoundPacket( 11 | ServerBoundPacketType.CLEAR_RESULT_REPLY, serializer.serializeTree(this)); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/runner/src/main/java/de/aaaaaaah/velcom/runner/states/Idle.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.runner.states; 2 | 3 | import de.aaaaaaah.velcom.runner.Connection; 4 | import de.aaaaaaah.velcom.runner.TeleBackend; 5 | 6 | /** 7 | * This is the (only) resting state in the runner state machine. It has no special behaviour 8 | * otherwise. 9 | */ 10 | public class Idle extends RunnerState { 11 | 12 | public Idle(TeleBackend teleBackend, Connection connection) { 13 | super(teleBackend, connection); 14 | } 15 | 16 | @Override 17 | public boolean isResting() { 18 | return true; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /frontend/src/util/Arrays.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Removes duplicated elements. 3 | * 4 | * @param input the input array 5 | */ 6 | export function distinct(input: T[]): T[] { 7 | const set = input.reduce((acc, next) => acc.add(next), new Set()); 8 | return Array.from(set.values()); 9 | } 10 | 11 | /** 12 | * Sorts an array of strings using the local comparator with base sensitivity. 13 | * 14 | * @param input the input array 15 | */ 16 | export function locallySorted(input: string[]): string[] { 17 | return input.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" })); 18 | } 19 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/protocol/serialization/clientbound/ClientBound.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.serialization.clientbound; 2 | 3 | import de.aaaaaaah.velcom.shared.protocol.serialization.Serializer; 4 | 5 | /** Packets that can be converted into a {@link ClientBoundPacket}. */ 6 | public interface ClientBound { 7 | 8 | /** 9 | * Convert this packet to a {@link ClientBoundPacket}. 10 | * 11 | * @param serializer the JSON serializer instance to use 12 | * @return the {@link ClientBoundPacket} 13 | */ 14 | ClientBoundPacket asPacket(Serializer serializer); 15 | } 16 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/protocol/serialization/serverbound/ServerBound.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.serialization.serverbound; 2 | 3 | import de.aaaaaaah.velcom.shared.protocol.serialization.Serializer; 4 | 5 | /** Packets that can be converted into a {@link ServerBoundPacket}. */ 6 | public interface ServerBound { 7 | 8 | /** 9 | * Convert this packet to a {@link ServerBoundPacket}. 10 | * 11 | * @param serializer the JSON serializer instance to use 12 | * @return the {@link ServerBoundPacket} 13 | */ 14 | ServerBoundPacket asPacket(Serializer serializer); 15 | } 16 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/protocol/serialization/clientbound/ClearResult.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.serialization.clientbound; 2 | 3 | import de.aaaaaaah.velcom.shared.protocol.serialization.Serializer; 4 | 5 | /** 6 | * A command to clear the current result. Must only be sent if the runner currently holds a result. 7 | */ 8 | public class ClearResult implements ClientBound { 9 | 10 | @Override 11 | public ClientBoundPacket asPacket(Serializer serializer) { 12 | return new ClientBoundPacket( 13 | ClientBoundPacketType.CLEAR_RESULT, serializer.serializeTree(this)); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/protocol/serialization/clientbound/GetResult.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.serialization.clientbound; 2 | 3 | import de.aaaaaaah.velcom.shared.protocol.serialization.Serializer; 4 | 5 | /** 6 | * A command requesting the current results from the runner. Must only be sent if the runner 7 | * currently holds a result. 8 | */ 9 | public class GetResult implements ClientBound { 10 | 11 | @Override 12 | public ClientBoundPacket asPacket(Serializer serializer) { 13 | return new ClientBoundPacket(ClientBoundPacketType.GET_RESULT, serializer.serializeTree(this)); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/protocol/serialization/serverbound/RequestRun.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.serialization.serverbound; 2 | 3 | import de.aaaaaaah.velcom.shared.protocol.serialization.Serializer; 4 | 5 | /** 6 | * A command that allows the backend to send the runner a new benchmark repo version and a new repo 7 | * to benchmark. 8 | */ 9 | public class RequestRun implements ServerBound { 10 | 11 | @Override 12 | public ServerBoundPacket asPacket(Serializer serializer) { 13 | return new ServerBoundPacket(ServerBoundPacketType.REQUEST_RUN, serializer.serializeTree(this)); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/util/Debouncer.ts: -------------------------------------------------------------------------------- 1 | // fitted to allow somewhat pleasant graph panning/reloading-cycle 2 | export const defaultWaitTime = 200; 3 | 4 | export function debounce any>( 5 | func: F, 6 | waitTime: number, 7 | ): (...args: Parameters) => ReturnType { 8 | let timeout: ReturnType | null = null; 9 | 10 | const debounced = (...args: Parameters) => { 11 | if (timeout !== null) { 12 | clearTimeout(timeout); 13 | timeout = null; 14 | } 15 | timeout = setTimeout(() => func(...args), waitTime); 16 | }; 17 | 18 | return debounced as (...args: Parameters) => ReturnType; 19 | } 20 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/protocol/RunnerConnectionHeader.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol; 2 | 3 | /** A collection of shared header names needed during the runner-backend connection. */ 4 | public enum RunnerConnectionHeader { 5 | CONNECT_RUNNER_TOKEN("Runner-Token"), 6 | CONNECT_RUNNER_NAME("Runner-Name"), 7 | DISCONNECT_DENY_REASON("Runner-Deny"); 8 | 9 | private final String headerName; 10 | 11 | RunnerConnectionHeader(String headerName) { 12 | this.headerName = headerName; 13 | } 14 | 15 | /** 16 | * @return the name of the header 17 | */ 18 | public String getName() { 19 | return headerName; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/access/repoaccess/exceptions/NoSuchRepoException.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.access.repoaccess.exceptions; 2 | 3 | import de.aaaaaaah.velcom.backend.access.repoaccess.entities.RepoId; 4 | 5 | /** This exception is thrown whenever an invalid {@link RepoId} is used. */ 6 | public class NoSuchRepoException extends RuntimeException { 7 | 8 | private final RepoId invalidId; 9 | 10 | public NoSuchRepoException(Throwable t, RepoId invalidId) { 11 | super("no repo with id " + invalidId, t); 12 | this.invalidId = invalidId; 13 | } 14 | 15 | public RepoId getInvalidId() { 16 | return invalidId; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/protocol/serialization/serverbound/AbortRunReply.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.serialization.serverbound; 2 | 3 | import de.aaaaaaah.velcom.shared.protocol.serialization.Serializer; 4 | 5 | /** 6 | * The reply to {@link de.aaaaaaah.velcom.shared.protocol.serialization.clientbound.AbortRun}. 7 | * Contains no additional information. 8 | */ 9 | public class AbortRunReply implements ServerBound { 10 | 11 | @Override 12 | public ServerBoundPacket asPacket(Serializer serializer) { 13 | return new ServerBoundPacket( 14 | ServerBoundPacketType.ABORT_RUN_REPLY, serializer.serializeTree(this)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /backend/shared/src/test/java/de/aaaaaaah/velcom/shared/util/PairTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.util; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertNotEquals; 5 | 6 | import org.junit.jupiter.api.Test; 7 | 8 | class PairTest { 9 | 10 | @Test 11 | void testContents() { 12 | Pair pair = new Pair<>(33, true); 13 | assertEquals(33, pair.getFirst()); 14 | assertEquals(true, pair.getSecond()); 15 | 16 | assertEquals(new Pair<>(33, true), pair); 17 | assertNotEquals(new Pair<>(33, false), pair); 18 | assertNotEquals(new Pair<>(34, true), pair); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/storage/db/DBReadAccess.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.storage.db; 2 | 3 | import java.io.Closeable; 4 | import java.util.Objects; 5 | import org.jooq.DSLContext; 6 | 7 | /** Allows read only access to a database. */ 8 | public class DBReadAccess implements Closeable { 9 | 10 | protected final DSLContext ctx; 11 | 12 | public DBReadAccess(DSLContext ctx) { 13 | this.ctx = Objects.requireNonNull(ctx); 14 | } 15 | 16 | @Override 17 | public void close() { 18 | // No longer needed 19 | // TODO: Remove this and clear up all usages? 20 | } 21 | 22 | public DSLContext dsl() { 23 | return ctx; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/util/ExceptionHelper.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.util; 2 | 3 | import java.io.PrintWriter; 4 | import java.io.StringWriter; 5 | 6 | /** Helps with common exception operations. */ 7 | public class ExceptionHelper { 8 | 9 | /** 10 | * Returns the stacktrace of a {@link Throwable} as a String. 11 | * 12 | * @param throwable the throwable 13 | * @return the stacktrace 14 | */ 15 | public static String getStackTrace(Throwable throwable) { 16 | StringWriter stringWriter = new StringWriter(); 17 | throwable.printStackTrace(new PrintWriter(stringWriter)); 18 | 19 | return stringWriter.toString(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/util/json/NewJsonHelper.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ 2 | import { RunDescriptionWithDifferences } from "@/store/types"; 3 | import { differenceFromJson, runDescriptionFromJson } from "@/util/json/CommitComparisonJsonHelper"; 4 | import { dimensionFromJson } from "@/util/json/RepoJsonHelper"; 5 | 6 | export function runDescriptionWithDifferencesFromJson(json: any): RunDescriptionWithDifferences { 7 | return new RunDescriptionWithDifferences( 8 | runDescriptionFromJson(json.run), 9 | (json.significant_differences || []).map(differenceFromJson), 10 | (json.significant_failed_dimensions || []).map(dimensionFromJson), 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/util/CheckedConsumer.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.util; 2 | 3 | /** 4 | * Represents an operation that accepts a single input argument, returns no result but could 5 | * possibly throw an exception. 6 | * 7 | * @param the type of the input to the operation 8 | * @param the type of the exception 9 | */ 10 | @FunctionalInterface 11 | public interface CheckedConsumer { 12 | 13 | /** 14 | * Performs this operation on the given argument. 15 | * 16 | * @param t the input argument 17 | * @throws E an exception if any occurs during the execution of the operation 18 | */ 19 | void accept(T t) throws E; 20 | } 21 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/util/execution/StreamsProcessOutput.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.util.execution; 2 | 3 | import java.util.concurrent.Future; 4 | 5 | /** 6 | * A future that also provides snapshots of the current standard out and standard error of the 7 | * process it waits on. 8 | * 9 | * @param The result type returned by this Future's get method 10 | */ 11 | public interface StreamsProcessOutput extends Future { 12 | 13 | /** 14 | * @return a snapshot of the current standard output of the process 15 | */ 16 | String getCurrentStdOut(); 17 | 18 | /** 19 | * @return a snapshot of the current standard error 20 | */ 21 | String getCurrentStdErr(); 22 | } 23 | -------------------------------------------------------------------------------- /frontend/src/util/Measurements.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Returns the height of an HTML element without any border, padding or margin. 3 | * 4 | * This might might be slow, as it uses the computed style and parses numbers 5 | * left and right. 6 | * 7 | * @param element the element to get the size for 8 | */ 9 | export function getInnerHeight(element: Element): number { 10 | const style = getComputedStyle(element); 11 | let height: number = element.clientHeight; 12 | 13 | height -= parseFloat(style.paddingTop) + parseFloat(style.paddingBottom); 14 | height -= parseFloat(style.marginTop) + parseFloat(style.marginBottom); 15 | height -= parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth); 16 | 17 | return height; 18 | } 19 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "noImplicitAny": true, 7 | "jsx": "preserve", 8 | "importHelpers": true, 9 | "moduleResolution": "node", 10 | "experimentalDecorators": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "sourceMap": true, 14 | "baseUrl": ".", 15 | "types": ["webpack-env", "vuetify"], 16 | "paths": { 17 | "@/*": ["src/*"] 18 | }, 19 | "lib": ["esnext", "dom", "dom.iterable", "scripthost"] 20 | }, 21 | "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "tests/**/*.ts", "tests/**/*.tsx"], 22 | "exclude": ["node_modules"] 23 | } 24 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/protocol/serialization/clientbound/AbortRun.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.serialization.clientbound; 2 | 3 | import de.aaaaaaah.velcom.shared.protocol.serialization.Serializer; 4 | 5 | /** 6 | * The client should abort the run it is currently doing for the backend. The client doesn't give 7 | * any guarantees to actually abort the run. If the client isn't performing a run for the backend, 8 | * this has no effect. 9 | */ 10 | public class AbortRun implements ClientBound { 11 | 12 | @Override 13 | public ClientBoundPacket asPacket(Serializer serializer) { 14 | return new ClientBoundPacket(ClientBoundPacketType.ABORT_RUN, serializer.serializeTree(this)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/restapi/jsonobjects/JsonDimensionId.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.jsonobjects; 2 | 3 | import de.aaaaaaah.velcom.backend.access.dimensionaccess.entities.Dimension; 4 | 5 | public class JsonDimensionId { 6 | 7 | private final String benchmark; 8 | private final String metric; 9 | 10 | public JsonDimensionId(String benchmark, String metric) { 11 | this.benchmark = benchmark; 12 | this.metric = metric; 13 | } 14 | 15 | public String getBenchmark() { 16 | return benchmark; 17 | } 18 | 19 | public String getMetric() { 20 | return metric; 21 | } 22 | 23 | public Dimension toDimension() { 24 | return new Dimension(getBenchmark(), getMetric()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /backend/backend/src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | , , 2 | / \/ \ 3 | (/ //_ \_ 4 | .-._ \|| . \ 5 | \ '-._ _,:__.-"/---\_ \ 6 | ______/___ '. .--------------------'~-'--.)__( , )\ \ 7 | `'--.___ _\ / | VELOCITY ,' \)|\ `\| 8 | /_.-' _\ \ _:,_ COMMIT " || ( 9 | .'__ _.' \'-/,`-~` brought to you by |/ 10 | '. ___.> /=,| Aaaaaaah | 11 | / .-'/_ ) '---------------------------------' 12 | snd )' ( /(/ 13 | \\ " 14 | '==' 15 | -------------------------------------------------------------------------------- /frontend/src/util/Errors.ts: -------------------------------------------------------------------------------- 1 | import { AxiosResponse } from "axios"; 2 | 3 | export interface AxiosError { 4 | response: AxiosResponse | undefined; 5 | message: string | undefined; 6 | } 7 | 8 | export function extractErrorMessage(error: AxiosError): string { 9 | if (error.response) { 10 | const errorMessage: undefined | string = 11 | error.response.data.message || error.response.data.error; 12 | if (errorMessage) { 13 | return `${errorMessage} (${error.response.status})`; 14 | } else if (error.response.status === 401 || error.response.status === 403) { 15 | return `Unauthorized! Please log in (${error.response.status})`; 16 | } 17 | return `Received error ${error.response.status}`; 18 | } 19 | return error.message || "Unknown error"; 20 | } 21 | -------------------------------------------------------------------------------- /backend/shared/src/test/java/de/aaaaaaah/velcom/shared/protocol/serialization/SerializerBasedTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.serialization; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.junit.jupiter.api.BeforeEach; 5 | 6 | /** Base class for tests that need a {@link Serializer}. */ 7 | public abstract class SerializerBasedTest { 8 | 9 | /** Use this to serialize or deserialize your data. */ 10 | protected Serializer serializer; 11 | 12 | /** Use this to create {@link com.fasterxml.jackson.databind.JsonNode}s whenever necessary. */ 13 | protected ObjectMapper objectMapper; 14 | 15 | @BeforeEach 16 | void setUpObjectMapper() { 17 | serializer = new Serializer(); 18 | objectMapper = new ObjectMapper(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/runner/IDispatcher.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.runner; 2 | 3 | import de.aaaaaaah.velcom.shared.util.LinesWithOffset; 4 | import java.util.List; 5 | import java.util.Optional; 6 | import java.util.UUID; 7 | 8 | /** The dispatcher interface other modules can compile against. */ 9 | public interface IDispatcher { 10 | 11 | /** 12 | * @return a list with all known runners 13 | */ 14 | List getKnownRunners(); 15 | 16 | /** 17 | * @param taskId the task to find the last log lines for 18 | * @return the last output lines for a given task. Considers live runners and the last few 19 | * finished results of each runner. 20 | */ 21 | Optional findLinesForTask(UUID taskId); 22 | } 23 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/util/CheckedFunction.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.util; 2 | 3 | /** 4 | * Represents a function that accepts a single input argument, returns a result and could possibly 5 | * throw an exception. 6 | * 7 | * @param the type of the input to the function 8 | * @param the type of the function's return value 9 | * @param the type of the exception 10 | */ 11 | @FunctionalInterface 12 | public interface CheckedFunction { 13 | 14 | /** 15 | * Performs this operation on the given argument. 16 | * 17 | * @param t the input argument 18 | * @return R on successful evaulation 19 | * @throws E an exception if any occurs during the execution of the operation 20 | */ 21 | R accept(T t) throws E; 22 | } 23 | -------------------------------------------------------------------------------- /backend/runner/src/test/java/de/aaaaaaah/velcom/runner/GitPropertiesTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.runner; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import de.aaaaaaah.velcom.shared.GitProperties; 6 | import org.assertj.core.api.Assertions; 7 | import org.junit.jupiter.api.Test; 8 | 9 | class GitPropertiesTest { 10 | 11 | @Test 12 | void fieldsPresent() { 13 | Assertions.assertThat(GitProperties.getHash()).isNotBlank(); 14 | assertThat(GitProperties.getHashAbbrev()).isNotBlank(); 15 | assertThat(GitProperties.getBuildTime()).isNotBlank(); 16 | assertThat(GitProperties.getVersion()).isNotBlank(); 17 | } 18 | 19 | @Test 20 | void hasAbbreviationIsAbbreviation() { 21 | assertThat(GitProperties.getHash()).startsWith(GitProperties.getHashAbbrev()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/restapi/endpoints/TestTokenEndpoint.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.endpoints; 2 | 3 | import de.aaaaaaah.velcom.backend.restapi.authentication.Admin; 4 | import io.dropwizard.auth.Auth; 5 | import io.micrometer.core.annotation.Timed; 6 | import javax.ws.rs.GET; 7 | import javax.ws.rs.Path; 8 | 9 | /** Endpoint for testing validity of access tokens. */ 10 | @Path("/test-token") 11 | public class TestTokenEndpoint { 12 | 13 | public TestTokenEndpoint() {} 14 | 15 | /** 16 | * Checks whether an access token is valid for a given repo. 17 | * 18 | * @param admin auth guard 19 | */ 20 | @GET 21 | @Timed(histogram = true) 22 | public void get(@Auth Admin admin) { 23 | // Being useful by doing nothing, when does that ever happen... 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /backend/backend/src/main/resources/db/migration/V9__latest_runs_view.sql: -------------------------------------------------------------------------------- 1 | CREATE VIEW latest_run ( 2 | id, 3 | author, 4 | runner_name, 5 | runner_info, 6 | start_time, 7 | stop_time, 8 | repo_id, 9 | commit_hash, 10 | tar_desc, 11 | error_type, 12 | error 13 | ) AS 14 | SELECT 15 | id, 16 | author, 17 | runner_name, 18 | runner_info, 19 | MAX(start_time), -- sqlite-specific 20 | stop_time, 21 | repo_id, 22 | commit_hash, 23 | tar_desc, 24 | error_type, 25 | error 26 | FROM run 27 | WHERE commit_hash IS NOT NULL 28 | GROUP BY repo_id, commit_hash 29 | UNION 30 | SELECT 31 | id, 32 | author, 33 | runner_name, 34 | runner_info, 35 | start_time, 36 | stop_time, 37 | repo_id, 38 | commit_hash, 39 | tar_desc, 40 | error_type, 41 | error 42 | FROM run 43 | WHERE commit_hash IS NULL; 44 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/access/taskaccess/exceptions/NoSuchTaskException.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.access.taskaccess.exceptions; 2 | 3 | import de.aaaaaaah.velcom.backend.access.taskaccess.entities.TaskId; 4 | 5 | /** This exception is thrown whenever an invalid {@link TaskId} is used. */ 6 | public class NoSuchTaskException extends RuntimeException { 7 | 8 | private final TaskId invalidId; 9 | 10 | public NoSuchTaskException(TaskId invalidId) { 11 | super("no task with id " + invalidId); 12 | this.invalidId = invalidId; 13 | } 14 | 15 | public NoSuchTaskException(Throwable t, TaskId invalidId) { 16 | super("no task with id " + invalidId, t); 17 | this.invalidId = invalidId; 18 | } 19 | 20 | public TaskId getInvalidId() { 21 | return invalidId; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /backend/runner/src/main/resources/example_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "_comment": { 3 | "info": "For more details, see https://github.com/IPDSnelting/velcom/blob/main/docs/install_manual.md#runner", 4 | "name": "The name of the runner. Must be unique across runners.", 5 | "backends": { 6 | "_comment": "A list of backends this runner will connect to.", 7 | "address": "The address at which this backend can be reached.", 8 | "token": "The token used to authenticate with the backend.", 9 | "directory": "The runner will place all its files for this backend in this directory." 10 | } 11 | }, 12 | 13 | "name": "Test runner", 14 | "backends": [ 15 | { 16 | "address": "ws://localhost:3546/runner-connector", 17 | "token": "Correct-Horse_Battery Staple", 18 | "directory": "localhost/" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/util/StringHelper.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.util; 2 | 3 | /** A utility class for manipulating strings. */ 4 | public class StringHelper { 5 | 6 | /** 7 | * Wrap a string in quotation marks. Escape quotation marks and backslashes in the string to make 8 | * the quotation unambiguous. Also escape some whitespace. 9 | * 10 | * @param string the string to wrap in quotationMarks 11 | * @return the quoted (and escaped) string 12 | */ 13 | public static String quote(String string) { 14 | String escaped = 15 | string 16 | .replace("\\", "\\\\") 17 | .replace("\"", "\\\"") 18 | .replace("\n", "\\n") 19 | .replace("\t", "\\t") 20 | .replace("\b", "\\b"); 21 | 22 | return "\"" + escaped + "\""; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/restapi/exception/TaskAlreadyExistsException.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.exception; 2 | 3 | import de.aaaaaaah.velcom.backend.access.committaccess.entities.CommitHash; 4 | import de.aaaaaaah.velcom.backend.access.repoaccess.entities.RepoId; 5 | 6 | /** Thrown when a task already exists in the queue but is added again. */ 7 | public class TaskAlreadyExistsException extends RuntimeException { 8 | 9 | private final CommitHash hash; 10 | private final RepoId repoId; 11 | 12 | public TaskAlreadyExistsException(CommitHash hash, RepoId repoId) { 13 | super("task already exists"); 14 | this.hash = hash; 15 | this.repoId = repoId; 16 | } 17 | 18 | public CommitHash getHash() { 19 | return hash; 20 | } 21 | 22 | public RepoId getRepoId() { 23 | return repoId; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/storage/repo/exception/DirectoryAlreadyExistsException.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.storage.repo.exception; 2 | 3 | import java.nio.file.Path; 4 | import java.util.Objects; 5 | 6 | /** An exception that is thrown when a certain directory already exists. */ 7 | public class DirectoryAlreadyExistsException extends Exception { 8 | 9 | private final Path directory; 10 | 11 | /** 12 | * Constructs a new {@link DirectoryAlreadyExistsException}. 13 | * 14 | * @param directory the directory that already exists 15 | */ 16 | public DirectoryAlreadyExistsException(Path directory) { 17 | this.directory = Objects.requireNonNull(directory); 18 | } 19 | 20 | /** 21 | * @return Returns the directry that already exists 22 | */ 23 | public Path getDirectory() { 24 | return directory; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /frontend/src/vue-class-components.d.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 2 | import Vue from "vue"; 3 | import { Route, RawLocation } from "vue-router"; 4 | 5 | declare module "vue/types/vue" { 6 | // Augment component instance type 7 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 8 | interface Vue { 9 | beforeRouteEnter?( 10 | to: Route, 11 | from: Route, 12 | next: (to?: RawLocation | false | ((vm: Vue) => any) | void) => void, 13 | ): void; 14 | 15 | beforeRouteLeave?( 16 | to: Route, 17 | from: Route, 18 | next: (to?: RawLocation | false | ((vm: Vue) => any) | void) => void, 19 | ): void; 20 | 21 | beforeRouteUpdate?( 22 | to: Route, 23 | from: Route, 24 | next: (to?: RawLocation | false | ((vm: Vue) => any) | void) => void, 25 | ): void; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/restapi/endpoints/ListenerEndpoint.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.endpoints; 2 | 3 | import de.aaaaaaah.velcom.backend.listener.Listener; 4 | import de.aaaaaaah.velcom.backend.restapi.authentication.Admin; 5 | import io.dropwizard.auth.Auth; 6 | import io.micrometer.core.annotation.Timed; 7 | import javax.ws.rs.POST; 8 | import javax.ws.rs.Path; 9 | import javax.ws.rs.Produces; 10 | import javax.ws.rs.core.MediaType; 11 | 12 | @Path("/listener") 13 | @Produces(MediaType.APPLICATION_JSON) 14 | public class ListenerEndpoint { 15 | 16 | private final Listener listener; 17 | 18 | public ListenerEndpoint(Listener listener) { 19 | this.listener = listener; 20 | } 21 | 22 | @POST 23 | @Path("/fetch-all") 24 | @Timed(histogram = true) 25 | public void post(@Auth Admin admin) { 26 | listener.updateAllRepos(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/restapi/authentication/AdminAuthenticator.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.authentication; 2 | 3 | import io.dropwizard.auth.Authenticator; 4 | import io.dropwizard.auth.basic.BasicCredentials; 5 | import java.util.Optional; 6 | 7 | public class AdminAuthenticator implements Authenticator { 8 | 9 | private final String adminToken; 10 | 11 | public AdminAuthenticator(String adminToken) { 12 | this.adminToken = adminToken; 13 | } 14 | 15 | @Override 16 | public Optional authenticate(BasicCredentials basicCredentials) { 17 | String username = basicCredentials.getUsername(); 18 | String password = basicCredentials.getPassword(); 19 | 20 | if (username.equals("admin") && password.equals(adminToken)) { 21 | return Optional.of(new Admin()); 22 | } 23 | 24 | return Optional.empty(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /backend/backend/src/test/java/de/aaaaaaah/velcom/backend/restapi/jsonobjects/JsonDimensionInfoTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.jsonobjects; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import de.aaaaaaah.velcom.backend.access.dimensionaccess.entities.Interpretation; 5 | import org.junit.jupiter.api.Test; 6 | 7 | class JsonDimensionInfoTest extends SerializingTest { 8 | 9 | @Test 10 | void serialize() throws JsonProcessingException { 11 | Object object = 12 | new JsonDimension("benchmarkName", "metricName", "someUnit", Interpretation.LESS_IS_BETTER); 13 | String json = 14 | "{" 15 | + "\"benchmark\": \"benchmarkName\"," 16 | + "\"metric\": \"metricName\"," 17 | + "\"unit\": \"someUnit\"," 18 | + "\"interpretation\": \"LESS_IS_BETTER\"" 19 | + "}"; 20 | serializedEquals(object, json); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/util/execution/ProgramResult.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.util.execution; 2 | 3 | import java.time.Duration; 4 | 5 | /** The result of executing a program. */ 6 | public class ProgramResult { 7 | 8 | private final int exitCode; 9 | private final String stdOut; 10 | private final String stdErr; 11 | private final Duration runtime; 12 | 13 | public ProgramResult(int exitCode, String stdOut, String stdErr, Duration runtime) { 14 | this.exitCode = exitCode; 15 | this.stdOut = stdOut; 16 | this.stdErr = stdErr; 17 | this.runtime = runtime; 18 | } 19 | 20 | public int getExitCode() { 21 | return exitCode; 22 | } 23 | 24 | public String getStdOut() { 25 | return stdOut; 26 | } 27 | 28 | public String getStdErr() { 29 | return stdErr; 30 | } 31 | 32 | public Duration getRuntime() { 33 | return runtime; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /frontend/src/store/modules/runSearchStore.ts: -------------------------------------------------------------------------------- 1 | import { action, createModule } from "vuex-class-component"; 2 | import axios from "axios"; 3 | import { RepoId, SearchItem } from "@/store/types"; 4 | import { searchItemsFromJson } from "@/util/json/RunSearchJsonHelper"; 5 | 6 | const VxModule = createModule({ 7 | namespaced: "runSearchModule", 8 | strict: false, 9 | }); 10 | 11 | export type RunQuery = { 12 | limit?: number; 13 | repoId?: RepoId; 14 | query: string; 15 | }; 16 | 17 | export class RunSearchStore extends VxModule { 18 | @action 19 | public async searchRun(query: RunQuery): Promise { 20 | const response = await axios.get("/search", { 21 | hideFromSnackbar: true, 22 | params: { 23 | limit: query.limit || 100, 24 | repo_id: query.repoId, 25 | query: query.query, 26 | }, 27 | }); 28 | 29 | return searchItemsFromJson(response.data); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/access/dimensionaccess/exceptions/NoSuchDimensionException.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.access.dimensionaccess.exceptions; 2 | 3 | import de.aaaaaaah.velcom.backend.access.dimensionaccess.entities.Dimension; 4 | 5 | /** This exception is thrown whenever an invalid {@link Dimension} is used. */ 6 | public class NoSuchDimensionException extends RuntimeException { 7 | 8 | private final Dimension invalidDimension; 9 | 10 | public NoSuchDimensionException(Dimension invalidDimension) { 11 | super("no dimension " + invalidDimension); 12 | this.invalidDimension = invalidDimension; 13 | } 14 | 15 | public NoSuchDimensionException(Throwable t, Dimension invalidDimension) { 16 | super("no dimension " + invalidDimension, t); 17 | this.invalidDimension = invalidDimension; 18 | } 19 | 20 | public Dimension getInvalidDimension() { 21 | return invalidDimension; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /backend/shared/src/test/java/de/aaaaaaah/velcom/shared/protocol/serialization/clientbound/AbortRunTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.serialization.clientbound; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertTrue; 5 | 6 | import com.fasterxml.jackson.databind.JsonNode; 7 | import de.aaaaaaah.velcom.shared.protocol.serialization.SerializerBasedTest; 8 | import java.util.Optional; 9 | import org.junit.jupiter.api.Test; 10 | 11 | class AbortRunTest extends SerializerBasedTest { 12 | 13 | @Test 14 | void serializeToEmptyObject() { 15 | JsonNode tree = serializer.serializeTree(new AbortRun()); 16 | assertEquals(objectMapper.createObjectNode(), tree); 17 | } 18 | 19 | @Test 20 | void deserializeFromEmptyObject() { 21 | Optional result = serializer.deserialize("{}", AbortRun.class); 22 | assertTrue(result.isPresent()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /frontend/src/components/misc/InlineMinimalRepoDisplay.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 27 | 28 | 33 | -------------------------------------------------------------------------------- /backend/shared/src/test/java/de/aaaaaaah/velcom/shared/protocol/serialization/clientbound/GetResultTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.serialization.clientbound; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertTrue; 5 | 6 | import com.fasterxml.jackson.databind.JsonNode; 7 | import de.aaaaaaah.velcom.shared.protocol.serialization.SerializerBasedTest; 8 | import java.util.Optional; 9 | import org.junit.jupiter.api.Test; 10 | 11 | class GetResultTest extends SerializerBasedTest { 12 | 13 | @Test 14 | void serializeToEmptyObject() { 15 | JsonNode tree = serializer.serializeTree(new GetResult()); 16 | assertEquals(objectMapper.createObjectNode(), tree); 17 | } 18 | 19 | @Test 20 | void deserializeFromEmptyObject() { 21 | Optional result = serializer.deserialize("{}", GetResult.class); 22 | assertTrue(result.isPresent()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /backend/shared/src/test/java/de/aaaaaaah/velcom/shared/protocol/serialization/clientbound/GetStatusTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.serialization.clientbound; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertTrue; 5 | 6 | import com.fasterxml.jackson.databind.JsonNode; 7 | import de.aaaaaaah.velcom.shared.protocol.serialization.SerializerBasedTest; 8 | import java.util.Optional; 9 | import org.junit.jupiter.api.Test; 10 | 11 | class GetStatusTest extends SerializerBasedTest { 12 | 13 | @Test 14 | void serializeToEmptyObject() { 15 | JsonNode tree = serializer.serializeTree(new GetStatus()); 16 | assertEquals(objectMapper.createObjectNode(), tree); 17 | } 18 | 19 | @Test 20 | void deserializeFromEmptyObject() { 21 | Optional result = serializer.deserialize("{}", GetStatus.class); 22 | assertTrue(result.isPresent()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /backend/shared/src/test/java/de/aaaaaaah/velcom/shared/protocol/serialization/serverbound/RequestRunTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.serialization.serverbound; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertTrue; 5 | 6 | import com.fasterxml.jackson.databind.JsonNode; 7 | import de.aaaaaaah.velcom.shared.protocol.serialization.SerializerBasedTest; 8 | import java.util.Optional; 9 | import org.junit.jupiter.api.Test; 10 | 11 | class RequestRunTest extends SerializerBasedTest { 12 | 13 | @Test 14 | void serializeToEmptyObject() { 15 | JsonNode tree = serializer.serializeTree(new RequestRun()); 16 | assertEquals(objectMapper.createObjectNode(), tree); 17 | } 18 | 19 | @Test 20 | void deserializeFromEmptyObject() { 21 | Optional result = serializer.deserialize("{}", RequestRun.class); 22 | assertTrue(result.isPresent()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /backend/shared/src/test/java/de/aaaaaaah/velcom/shared/protocol/serialization/clientbound/ClearResultTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.serialization.clientbound; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertTrue; 5 | 6 | import com.fasterxml.jackson.databind.JsonNode; 7 | import de.aaaaaaah.velcom.shared.protocol.serialization.SerializerBasedTest; 8 | import java.util.Optional; 9 | import org.junit.jupiter.api.Test; 10 | 11 | class ClearResultTest extends SerializerBasedTest { 12 | 13 | @Test 14 | void serializeToEmptyObject() { 15 | JsonNode tree = serializer.serializeTree(new ClearResult()); 16 | assertEquals(objectMapper.createObjectNode(), tree); 17 | } 18 | 19 | @Test 20 | void deserializeFromEmptyObject() { 21 | Optional result = serializer.deserialize("{}", ClearResult.class); 22 | assertTrue(result.isPresent()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /scripts/build-frontend: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import argparse 4 | import shlex 5 | import subprocess 6 | from pathlib import Path 7 | 8 | DIR: Path = Path("frontend") 9 | 10 | 11 | def run(*cmd: str | Path) -> None: 12 | print(f"$ {shlex.join(str(arg) for arg in cmd)}") 13 | subprocess.run(cmd, check=True, cwd=DIR) 14 | 15 | 16 | parser = argparse.ArgumentParser() 17 | parser.add_argument( 18 | "--env", 19 | metavar="NAME", 20 | help="name of the build environment to use. A corresponding file" 21 | " '.env.NAME' must be present in the frontend/ directory", 22 | ) 23 | args = parser.parse_args() 24 | 25 | run( 26 | "yarnpkg", 27 | "install", 28 | # Yarnpkg complains about incompatible node versions, but the build seems to 29 | # run fine, so we just tell it to shut up. 30 | "--ignore-engines", 31 | ) 32 | 33 | if args.env is None: 34 | run("yarnpkg", "build") 35 | else: 36 | run("yarnpkg", "build", "--mode", args.env) 37 | -------------------------------------------------------------------------------- /backend/shared/src/test/java/de/aaaaaaah/velcom/shared/protocol/serialization/serverbound/AbortRunReplyTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.serialization.serverbound; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertTrue; 5 | 6 | import com.fasterxml.jackson.databind.JsonNode; 7 | import de.aaaaaaah.velcom.shared.protocol.serialization.SerializerBasedTest; 8 | import java.util.Optional; 9 | import org.junit.jupiter.api.Test; 10 | 11 | class AbortRunReplyTest extends SerializerBasedTest { 12 | 13 | @Test 14 | void serializeToEmptyObject() { 15 | JsonNode tree = serializer.serializeTree(new AbortRunReply()); 16 | assertEquals(objectMapper.createObjectNode(), tree); 17 | } 18 | 19 | @Test 20 | void deserializeFromEmptyObject() { 21 | Optional result = serializer.deserialize("{}", AbortRunReply.class); 22 | assertTrue(result.isPresent()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /backend/shared/src/test/java/de/aaaaaaah/velcom/shared/protocol/serialization/serverbound/ClearResultReplyTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.serialization.serverbound; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertTrue; 5 | 6 | import com.fasterxml.jackson.databind.JsonNode; 7 | import de.aaaaaaah.velcom.shared.protocol.serialization.SerializerBasedTest; 8 | import java.util.Optional; 9 | import org.junit.jupiter.api.Test; 10 | 11 | class ClearResultReplyTest extends SerializerBasedTest { 12 | 13 | @Test 14 | void serializeToEmptyObject() { 15 | JsonNode tree = serializer.serializeTree(new ClearResultReply()); 16 | assertEquals(objectMapper.createObjectNode(), tree); 17 | } 18 | 19 | @Test 20 | void deserializeFromEmptyObject() { 21 | Optional result = serializer.deserialize("{}", ClearResultReply.class); 22 | assertTrue(result.isPresent()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/restapi/exception/InvalidQueryParamsExceptionMapper.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.exception; 2 | 3 | import javax.ws.rs.core.Response; 4 | import javax.ws.rs.core.Response.Status; 5 | import javax.ws.rs.ext.ExceptionMapper; 6 | 7 | /** 8 | * An {@link ExceptionMapper} that transforms {@link InvalidQueryParamsException}s to BAD_REQUEST. 9 | */ 10 | public class InvalidQueryParamsExceptionMapper 11 | implements ExceptionMapper { 12 | 13 | @Override 14 | public Response toResponse(InvalidQueryParamsException exception) { 15 | return Response.status(Status.BAD_REQUEST).entity(new Info(exception.getMessage())).build(); 16 | } 17 | 18 | private static class Info { 19 | 20 | private final String message; 21 | 22 | public Info(String message) { 23 | this.message = message; 24 | } 25 | 26 | public String getMessage() { 27 | return message; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/restapi/jsonobjects/JsonBranch.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.jsonobjects; 2 | 3 | import de.aaaaaaah.velcom.backend.access.repoaccess.entities.Branch; 4 | 5 | public class JsonBranch { 6 | 7 | private final String name; 8 | private final boolean tracked; 9 | private final String latest_commit; 10 | 11 | public JsonBranch(String name, boolean tracked, String latest_commit) { 12 | this.name = name; 13 | this.tracked = tracked; 14 | this.latest_commit = latest_commit; 15 | } 16 | 17 | public static JsonBranch fromBranch(Branch branch) { 18 | return new JsonBranch( 19 | branch.getName().getName(), branch.isTracked(), branch.getLatestCommitHash().getHash()); 20 | } 21 | 22 | public String getName() { 23 | return name; 24 | } 25 | 26 | public boolean isTracked() { 27 | return tracked; 28 | } 29 | 30 | public String getLatest_commit() { 31 | return latest_commit; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/runner/Delays.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.runner; 2 | 3 | import java.time.Duration; 4 | 5 | /** A class for keeping all kinds of runner-related delays and timeouts. */ 6 | public final class Delays { 7 | 8 | /** 9 | * How long to wait for a runner response when trying to close a connection before force-closing 10 | * it. 11 | */ 12 | public static final Duration CLOSE_CONNECTION_TIMEOUT = Duration.ofSeconds(10); 13 | 14 | /** How long to wait - after sending a command to the server - for a reply to that command. */ 15 | public static final Duration AWAIT_COMMAND_REPLY = Duration.ofSeconds(10); 16 | 17 | /** 18 | * How long to wait between "get_status" commands. This affects the update interval of the live 19 | * build log. 20 | */ 21 | public static final Duration REQUEST_STATUS_INTERVAL = Duration.ofSeconds(5); 22 | 23 | private Delays() { 24 | throw new UnsupportedOperationException(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /cli/velcom_cli/commands/print_config.py: -------------------------------------------------------------------------------- 1 | import io 2 | 3 | 4 | def register(subparsers): 5 | parser = subparsers.add_parser( 6 | "print-config", 7 | aliases=["pc"], 8 | help="print the currently active configuration on stdout", 9 | ) 10 | parser.set_defaults(f=command) 11 | 12 | 13 | def command(config): 14 | print("####################") 15 | print("## Current config ##") 16 | print("####################") 17 | print() 18 | 19 | with io.StringIO() as stream: 20 | config.parser.write(stream) 21 | print(stream.getvalue(), end="") 22 | 23 | print() 24 | 25 | profile_name = config.profile_name 26 | profile_hashes = "#" * len(profile_name) 27 | 28 | print("######################" + profile_hashes) 29 | print(f"## Active profile: {profile_name} ##") 30 | print("######################" + profile_hashes) 31 | print() 32 | 33 | for k, v in config.items(): 34 | print(f"{k} = {v}") 35 | 36 | print() 37 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/restapi/logging/MetricsEndpointFilter.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.logging; 2 | 3 | import ch.qos.logback.access.spi.IAccessEvent; 4 | import ch.qos.logback.core.filter.Filter; 5 | import ch.qos.logback.core.spi.FilterReply; 6 | import com.fasterxml.jackson.annotation.JsonTypeName; 7 | import io.dropwizard.logging.filter.FilterFactory; 8 | import org.kohsuke.MetaInfServices; 9 | 10 | @MetaInfServices(FilterFactory.class) 11 | @JsonTypeName("metrics-endpoint") 12 | public class MetricsEndpointFilter implements FilterFactory { 13 | 14 | @Override 15 | public Filter build() { 16 | return new Filter<>() { 17 | @Override 18 | public FilterReply decide(IAccessEvent event) { 19 | if (event.getRequestURI().equals("/prometheusMetrics")) { 20 | return FilterReply.DENY; 21 | } else { 22 | return FilterReply.NEUTRAL; 23 | } 24 | } 25 | }; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/access/committaccess/entities/CommitHash.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.access.committaccess.entities; 2 | 3 | import java.util.Objects; 4 | 5 | /** A git commit hash (40 characters of hexadecimal digits). */ 6 | public class CommitHash { 7 | 8 | private final String hash; 9 | 10 | public CommitHash(String hash) { 11 | this.hash = hash; 12 | } 13 | 14 | public String getHash() { 15 | return hash; 16 | } 17 | 18 | @Override 19 | public boolean equals(Object o) { 20 | if (this == o) { 21 | return true; 22 | } 23 | if (o == null || getClass() != o.getClass()) { 24 | return false; 25 | } 26 | CommitHash that = (CommitHash) o; 27 | return Objects.equals(hash, that.hash); 28 | } 29 | 30 | @Override 31 | public int hashCode() { 32 | return Objects.hash(hash); 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "CommitHash{" + "hash='" + hash + '\'' + '}'; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/access/committaccess/exceptions/NoSuchCommitException.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.access.committaccess.exceptions; 2 | 3 | import de.aaaaaaah.velcom.backend.access.committaccess.entities.CommitHash; 4 | import de.aaaaaaah.velcom.backend.access.repoaccess.entities.RepoId; 5 | 6 | /** 7 | * This exception is thrown whenever an invalid combination of {@link RepoId} and {@link CommitHash} 8 | * is used. 9 | */ 10 | public class NoSuchCommitException extends RuntimeException { 11 | 12 | private final RepoId repoId; 13 | private final CommitHash commitHash; 14 | 15 | public NoSuchCommitException(Throwable t, RepoId repoId, CommitHash commitHash) { 16 | super("no commit in repo " + repoId + " with hash " + commitHash, t); 17 | 18 | this.repoId = repoId; 19 | this.commitHash = commitHash; 20 | } 21 | 22 | public RepoId getRepoId() { 23 | return repoId; 24 | } 25 | 26 | public CommitHash getCommitHash() { 27 | return commitHash; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/restapi/exception/ArgumentParseExceptionMapper.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.exception; 2 | 3 | import de.aaaaaaah.velcom.backend.restapi.endpoints.utils.ArgumentParseException; 4 | import javax.ws.rs.core.Response; 5 | import javax.ws.rs.core.Response.Status; 6 | import javax.ws.rs.ext.ExceptionMapper; 7 | 8 | /** An {@link ExceptionMapper} that transforms {@link ArgumentParseException}s to BAD_REQUEST. */ 9 | public class ArgumentParseExceptionMapper implements ExceptionMapper { 10 | 11 | @Override 12 | public Response toResponse(ArgumentParseException exception) { 13 | return Response.status(Status.BAD_REQUEST).entity(new Info(exception.getMessage())).build(); 14 | } 15 | 16 | private static class Info { 17 | 18 | private final String message; 19 | 20 | public Info(String message) { 21 | this.message = message; 22 | } 23 | 24 | public String getMessage() { 25 | return message; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/storage/db/DBWriteAccess.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.storage.db; 2 | 3 | import java.util.Objects; 4 | import java.util.concurrent.locks.Lock; 5 | import org.jooq.DSLContext; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | /** Allows read and write access to a database. */ 10 | public class DBWriteAccess extends DBReadAccess { 11 | 12 | private static final Logger LOGGER = LoggerFactory.getLogger(DBWriteAccess.class); 13 | 14 | private final Lock lock; 15 | 16 | public DBWriteAccess(DSLContext ctx, Lock lock) { 17 | super(ctx); 18 | this.lock = Objects.requireNonNull(lock); 19 | this.lock.lock(); 20 | } 21 | 22 | @Override 23 | public void close() { 24 | try { 25 | super.close(); 26 | } finally { 27 | lock.unlock(); 28 | } 29 | } 30 | 31 | public void vacuum() { 32 | LOGGER.info("Vacuuming database..."); 33 | ctx.execute("VACUUM"); 34 | LOGGER.info("Vacuuming completed."); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/listener/InvalidRemoteUrlException.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.listener; 2 | 3 | /** 4 | * An exception thrown if a jgit repo doesn't have the remote url it should have. This exception was 5 | * created to avoid overly long pasta-like code in the {@link Listener}. 6 | */ 7 | public class InvalidRemoteUrlException extends RuntimeException { 8 | 9 | private final String realRemoteUrl; 10 | private final String targetRemoteUrl; 11 | 12 | public InvalidRemoteUrlException(String realRemoteUrl, String targetRemoteUrl) { 13 | super( 14 | "Real remote url " 15 | + realRemoteUrl 16 | + " does not match target remote url " 17 | + targetRemoteUrl); 18 | 19 | this.realRemoteUrl = realRemoteUrl; 20 | this.targetRemoteUrl = targetRemoteUrl; 21 | } 22 | 23 | public String getRealRemoteUrl() { 24 | return realRemoteUrl; 25 | } 26 | 27 | public String getTargetRemoteUrl() { 28 | return targetRemoteUrl; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /frontend/src/store/modules/cleanupStore.ts: -------------------------------------------------------------------------------- 1 | import { createModule, action } from "vuex-class-component"; 2 | import { CleanupDimension, Dimension } from "@/store/types"; 3 | import axios from "axios"; 4 | import { cleanupDimensionFromJson } from "@/util/json/CleanupJsonHelper"; 5 | 6 | const VxModule = createModule({ 7 | namespaced: "cleanup", 8 | strict: false, 9 | }); 10 | 11 | export class CleanupStore extends VxModule { 12 | dimensions: CleanupDimension[] | null = null; 13 | 14 | @action 15 | async fetchDimensions(): Promise { 16 | const response = await axios.get("/all-dimensions"); 17 | this.dimensions = response.data.dimensions.map(cleanupDimensionFromJson); 18 | return this.dimensions!; 19 | } 20 | 21 | @action 22 | async deleteDimensions(dimensions: Dimension[]): Promise { 23 | const dimensionIds = dimensions.map((it) => ({ 24 | metric: it.metric, 25 | benchmark: it.benchmark, 26 | })); 27 | 28 | await axios.delete(`/dimensions`, { data: dimensionIds }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/access/dimensionaccess/entities/Unit.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.access.dimensionaccess.entities; 2 | 3 | import java.util.Objects; 4 | 5 | /** A measurement unit that measurement values are given in. */ 6 | public class Unit { 7 | 8 | public static final Unit DEFAULT = new Unit(""); 9 | 10 | public Unit(String name) { 11 | this.name = Objects.requireNonNull(name); 12 | } 13 | 14 | private final String name; 15 | 16 | public String getName() { 17 | return name; 18 | } 19 | 20 | @Override 21 | public boolean equals(Object o) { 22 | if (this == o) { 23 | return true; 24 | } 25 | if (o == null || getClass() != o.getClass()) { 26 | return false; 27 | } 28 | Unit unit = (Unit) o; 29 | return name.equals(unit.name); 30 | } 31 | 32 | @Override 33 | public int hashCode() { 34 | return Objects.hash(name); 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return "Unit{" + "name='" + name + '\'' + '}'; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/listener/github/GithubCommandState.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.listener.github; 2 | 3 | import java.util.List; 4 | 5 | public enum GithubCommandState { 6 | NEW("NEW"), 7 | MARKED_SEEN("MARKED_SEEN"), 8 | QUEUED("QUEUED"), 9 | ERROR("ERROR"); 10 | 11 | private final String textualRepresentation; 12 | 13 | GithubCommandState(String textualRepresentation) { 14 | this.textualRepresentation = textualRepresentation; 15 | } 16 | 17 | public static GithubCommandState fromTextualRepresentation(String representation) 18 | throws IllegalArgumentException { 19 | 20 | for (GithubCommandState state : List.of(NEW, MARKED_SEEN, QUEUED, ERROR)) { 21 | if (state.getTextualRepresentation().equals(representation)) { 22 | return state; 23 | } 24 | } 25 | 26 | throw new IllegalArgumentException("\"" + representation + "\" is not a valid command state"); 27 | } 28 | 29 | public String getTextualRepresentation() { 30 | return textualRepresentation; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/data/recentruns/SignificantRun.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.data.recentruns; 2 | 3 | import de.aaaaaaah.velcom.backend.access.benchmarkaccess.entities.Run; 4 | import de.aaaaaaah.velcom.backend.data.runcomparison.DimensionDifference; 5 | import de.aaaaaaah.velcom.backend.data.significance.SignificanceReasons; 6 | 7 | /** 8 | * A run that has been found to be significant. Contains the significant {@link 9 | * DimensionDifference}s which caused this. 10 | */ 11 | public class SignificantRun { 12 | 13 | private final Run run; 14 | private final SignificanceReasons reasons; 15 | 16 | public SignificantRun(Run run, SignificanceReasons reasons) { 17 | this.run = run; 18 | this.reasons = reasons; 19 | } 20 | 21 | public Run getRun() { 22 | return run; 23 | } 24 | 25 | public SignificanceReasons getReasons() { 26 | return reasons; 27 | } 28 | 29 | @Override 30 | public String toString() { 31 | return "SignificantRun{" + "run=" + run + ", reasons=" + reasons + '}'; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/util/LinesWithOffset.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.util; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * A collection of lines including the offset of the first one. This is useful when you are only 7 | * working with an excerpt but want to preserve absolute line numbers. 8 | */ 9 | public class LinesWithOffset { 10 | 11 | private final int firstLineOffset; 12 | private final List lines; 13 | 14 | /** 15 | * If there are three lines and the last two are included in this object, the firstLineOffset will 16 | * be one (totalLength - includedLength). 17 | * 18 | * @param firstLineOffset the offset of the first line. 0 based. 19 | * @param lines the lines 20 | */ 21 | public LinesWithOffset(int firstLineOffset, List lines) { 22 | this.firstLineOffset = firstLineOffset; 23 | this.lines = lines; 24 | } 25 | 26 | public int getFirstLineOffset() { 27 | return firstLineOffset; 28 | } 29 | 30 | public List getLines() { 31 | return lines; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/listener/github/JsonBodyHandler.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.listener.github; 2 | 3 | import com.fasterxml.jackson.databind.JsonNode; 4 | import io.dropwizard.jackson.Jackson; 5 | import java.io.IOException; 6 | import java.io.UncheckedIOException; 7 | import java.net.http.HttpResponse; 8 | import java.net.http.HttpResponse.BodyHandler; 9 | import java.net.http.HttpResponse.BodySubscriber; 10 | import java.net.http.HttpResponse.ResponseInfo; 11 | import java.nio.charset.StandardCharsets; 12 | 13 | public class JsonBodyHandler implements BodyHandler { 14 | 15 | @Override 16 | public BodySubscriber apply(ResponseInfo responseInfo) { 17 | return HttpResponse.BodySubscribers.mapping( 18 | HttpResponse.BodySubscribers.ofString(StandardCharsets.UTF_8), 19 | s -> { 20 | try { 21 | return Jackson.newObjectMapper().readTree(s); 22 | } catch (IOException e) { 23 | throw new UncheckedIOException(e); 24 | } 25 | }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/runner/single/state/TimeoutState.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.runner.single.state; 2 | 3 | import de.aaaaaaah.velcom.backend.runner.Delays; 4 | import de.aaaaaaah.velcom.backend.runner.single.RunnerConnection; 5 | import de.aaaaaaah.velcom.backend.runner.single.TeleRunner; 6 | import de.aaaaaaah.velcom.shared.protocol.StatusCode; 7 | import de.aaaaaaah.velcom.shared.util.Timeout; 8 | 9 | /** A state that waits for a given timeout before closing the connection. */ 10 | public abstract class TimeoutState extends TeleRunnerState { 11 | 12 | private final Timeout timeout; 13 | 14 | public TimeoutState(TeleRunner runner, RunnerConnection connection) { 15 | super(runner, connection); 16 | 17 | timeout = Timeout.after(Delays.AWAIT_COMMAND_REPLY); 18 | timeout.getCompletionStage().thenRun(() -> connection.close(StatusCode.COMMAND_TIMEOUT)); 19 | } 20 | 21 | @Override 22 | public void onEnter() { 23 | timeout.start(); 24 | } 25 | 26 | @Override 27 | public void onExit() { 28 | timeout.cancel(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /backend/shared/src/test/java/de/aaaaaaah/velcom/shared/util/compression/PermissionsHelperTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.util.compression; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import java.nio.file.attribute.PosixFilePermission; 6 | import java.nio.file.attribute.PosixFilePermissions; 7 | import java.util.Set; 8 | import org.junit.jupiter.params.ParameterizedTest; 9 | import org.junit.jupiter.params.provider.CsvSource; 10 | 11 | class PermissionsHelperTest { 12 | 13 | @ParameterizedTest(name = "Mode {0} maps to \"{1}\"") 14 | @CsvSource({ 15 | "0100, --x------", 16 | "0111, --x--x--x", 17 | "0444, r--r--r--", 18 | "0222, -w--w--w-", 19 | "0421, r---w---x", 20 | "0461, r--rw---x", 21 | "0467, r--rw-rwx", 22 | }) 23 | void fewBaseTests(int mode, String permissionString) { 24 | Set permissions = PosixFilePermissions.fromString(permissionString); 25 | assertEquals(permissions, PermissionsHelper.fromOctal(mode)); 26 | assertEquals(mode, PermissionsHelper.toOctal(permissions)); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /backend/backend/src/test/java/de/aaaaaaah/velcom/backend/restapi/jsonobjects/JsonTaskTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.jsonobjects; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import java.util.UUID; 5 | import org.junit.jupiter.api.Test; 6 | 7 | class JsonTaskTest extends SerializingTest { 8 | 9 | @Test 10 | void serialize() throws JsonProcessingException { 11 | Object object = 12 | new JsonTask( 13 | UUID.fromString("24dd4fd3-5c6d-4542-a7a4-b181f37295a6"), 14 | "authorName", 15 | 1596881630, 16 | JsonSource.tarSource("descriptionText", null)); 17 | String json = 18 | "{" 19 | + "\"id\": \"24dd4fd3-5c6d-4542-a7a4-b181f37295a6\"," 20 | + "\"author\": \"authorName\"," 21 | + "\"since\": 1596881630," 22 | + "\"source\": {" 23 | + " \"type\": \"UPLOADED_TAR\"," 24 | + " \"source\": {\"description\": \"descriptionText\"}" 25 | + "}" 26 | + "}"; 27 | serializedEquals(object, json); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /backend/runner/src/main/java/de/aaaaaaah/velcom/runner/tmpdirs/TaskRepoDir.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.runner.tmpdirs; 2 | 3 | import de.aaaaaaah.velcom.shared.util.FileHelper; 4 | import java.io.IOException; 5 | import java.nio.file.Path; 6 | 7 | /** 8 | * This class manages the directory where the task repo is stored, as well as a temporary file used 9 | * while downloading and unpacking a new bench repo. 10 | */ 11 | public class TaskRepoDir { 12 | 13 | private final Path dirPath; 14 | private final Path tmpFilePath; 15 | 16 | public TaskRepoDir(Path dirPath) { 17 | this.dirPath = dirPath; 18 | tmpFilePath = dirPath.getParent().resolve(dirPath.getFileName() + ".tmp"); 19 | } 20 | 21 | /** 22 | * Delete the directory. 23 | * 24 | * @throws IOException if something io-related goes wrong during the deletion 25 | */ 26 | public void clear() throws IOException { 27 | FileHelper.deleteDirectoryOrFile(dirPath); 28 | } 29 | 30 | public Path getDirPath() { 31 | return dirPath; 32 | } 33 | 34 | public Path getTmpFilePath() { 35 | return tmpFilePath; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /backend/backend/src/test/java/de/aaaaaaah/velcom/backend/restapi/jsonobjects/JsonCommitDescriptionTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.jsonobjects; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import java.util.UUID; 5 | import org.junit.jupiter.api.Test; 6 | 7 | class JsonCommitDescriptionTest extends SerializingTest { 8 | 9 | @Test 10 | void serialize() throws JsonProcessingException { 11 | Object object = 12 | new JsonCommitDescription( 13 | UUID.fromString("24dd4fd3-5c6d-4542-a7a4-b181f37295a6"), 14 | "e16272feb472dc4d357cc19dd97112c036a67990", 15 | "authorName", 16 | 1596881630, 17 | "summaryText"); 18 | String json = 19 | "{" 20 | + "\"repo_id\": \"24dd4fd3-5c6d-4542-a7a4-b181f37295a6\"," 21 | + "\"hash\": \"e16272feb472dc4d357cc19dd97112c036a67990\"," 22 | + "\"author\": \"authorName\"," 23 | + "\"author_date\": 1596881630," 24 | + "\"summary\": \"summaryText\"" 25 | + "}"; 26 | serializedEquals(object, json); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Aaaaaaah 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/protocol/statemachine/State.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.statemachine; 2 | 3 | /** A state for the {@link StateMachine}. */ 4 | public interface State { 5 | 6 | /** 7 | * This function is called whenever the state is entered. It is also called on the first state 8 | * when a {@link StateMachine} is initialized. It exists because large amounts of time may pass 9 | * between creation of the state object and the {@link StateMachine} switching to said object. 10 | */ 11 | default void onEnter() {} 12 | 13 | /** 14 | * This function is called whenever the state is exited. It is also called when the {@link 15 | * StateMachine} is stopped. It can be used to clean up resources like files or timers. 16 | */ 17 | default void onExit() {} 18 | 19 | /** 20 | * If a state is resting, it can be switched out of at any time using the {@link 21 | * StateMachine#switchFromRestingState(State)} function. 22 | * 23 | * @return whether this state is a resting state 24 | */ 25 | default boolean isResting() { 26 | return false; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /scripts/docker/start-backend-docker.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This is a sample docker entrypoint. It is used by the provided docker image 4 | # to start up the backend. 5 | 6 | # Relevant parts: 7 | # 1. Start nginx AND our application 8 | # 2. Relay SIGTERM and SIGINT to our java application so the docker container 9 | # can be stopped properly 10 | 11 | # Start nginx for routing 12 | nginx & 13 | 14 | cd "/home/velcom" 15 | 16 | echo "Running in $PWD" 17 | 18 | # Start our backend 19 | if [ -f /home/velcom/aspectjweaver.jar ]; then 20 | sudo -Eu velcom $(which java) -javaagent:"/home/velcom/aspectjweaver.jar" -jar /home/velcom/velcom.jar server "/home/velcom/config/config.yml" & 21 | else 22 | sudo -Eu velcom $(which java) -jar /home/velcom/velcom.jar server "/home/velcom/config/config.yml" & 23 | fi 24 | 25 | # Save its PID so we can relay kill signals 26 | JAVA_PID="$!" 27 | 28 | # The shell does not pass on exit signals, so we do it manually 29 | # This ensures java is killed properly and the container can restart. 30 | trap "kill $JAVA_PID" exit INT TERM 31 | 32 | # Wait for the subprocesses (= spawned by &) to finish 33 | wait 34 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/listener/github/FinishedGithubCommand.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.listener.github; 2 | 3 | import de.aaaaaaah.velcom.backend.access.benchmarkaccess.entities.RunId; 4 | import de.aaaaaaah.velcom.backend.access.committaccess.entities.CommitHash; 5 | import de.aaaaaaah.velcom.backend.access.repoaccess.entities.BranchName; 6 | 7 | public class FinishedGithubCommand { 8 | 9 | private final long pr; 10 | private final BranchName targetBranch; 11 | private final CommitHash commitHash; 12 | private final RunId runId; 13 | 14 | public FinishedGithubCommand( 15 | long pr, BranchName targetBranch, CommitHash commitHash, RunId runId) { 16 | 17 | this.pr = pr; 18 | this.targetBranch = targetBranch; 19 | this.commitHash = commitHash; 20 | this.runId = runId; 21 | } 22 | 23 | public long getPr() { 24 | return pr; 25 | } 26 | 27 | public BranchName getTargetBranch() { 28 | return targetBranch; 29 | } 30 | 31 | public CommitHash getCommitHash() { 32 | return commitHash; 33 | } 34 | 35 | public RunId getRunId() { 36 | return runId; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /frontend/src/components/rundetail/MeasurementValueDisplay.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/protocol/RunnerDenyReason.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol; 2 | 3 | /** The possible reasons when the runner's connection attempt was denied. */ 4 | public enum RunnerDenyReason { 5 | TOKEN_INVALID(401, "your token is invalid", "TOKEN"), 6 | NAME_ALREADY_USED(403, "your name is already taken", "NAME"); 7 | 8 | private final int code; 9 | private final String message; 10 | private final String headerValue; 11 | 12 | RunnerDenyReason(int code, String message, String headerValue) { 13 | this.code = code; 14 | this.message = message; 15 | this.headerValue = headerValue; 16 | } 17 | 18 | /** 19 | * @return the value the "Runner-Deny" header will contain 20 | */ 21 | public String getHeaderValue() { 22 | return headerValue; 23 | } 24 | 25 | /** 26 | * @return the http response code associated with this event 27 | */ 28 | public int getCode() { 29 | return code; 30 | } 31 | 32 | /** 33 | * @return the reason the runner's connection attempt was denied as a human readable message 34 | */ 35 | public String getMessage() { 36 | return message; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /frontend/src/views/Settings.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 40 | -------------------------------------------------------------------------------- /frontend/src/util/Clipboards.ts: -------------------------------------------------------------------------------- 1 | import { ISnackbar } from "@/util/Snackbar"; 2 | 3 | /** 4 | * Copies the given text to the clipboard. 5 | * @param text the text to copy 6 | * @param snackbar the snackbar to use for status reporting 7 | */ 8 | export function copyToClipboard(text: string, snackbar: ISnackbar): void { 9 | if (navigator.clipboard) { 10 | navigator.clipboard 11 | .writeText(text) 12 | .then(() => snackbar.setSuccess("", "Copied!")) 13 | .catch((error) => snackbar.setError("", "Could not copy to clipboard :( " + error)); 14 | 15 | return; 16 | } 17 | 18 | // Compatibility for sites without a secure context 19 | // (or users that disable navigator.clipboard) 20 | 21 | // setup new node 22 | const node = document.createElement("span"); 23 | node.innerText = text; 24 | node.contentEditable = "true"; 25 | document.body.appendChild(node); 26 | 27 | node.focus({ preventScroll: true }); 28 | 29 | // Copy its text 30 | document.execCommand("SelectAll"); 31 | document.execCommand("copy"); 32 | 33 | // delete it 34 | document.body.removeChild(node); 35 | 36 | snackbar.setSuccess("", "Copied (using compatibility mode)!"); 37 | } 38 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/access/benchmarkaccess/entities/MeasurementError.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.access.benchmarkaccess.entities; 2 | 3 | import java.util.Objects; 4 | 5 | /** This class represents a failed {@link Measurement}'s state, which only contains an message. */ 6 | public class MeasurementError { 7 | 8 | private final String errorMessage; 9 | 10 | public MeasurementError(String errorMessage) { 11 | this.errorMessage = Objects.requireNonNull(errorMessage); 12 | } 13 | 14 | public String getErrorMessage() { 15 | return errorMessage; 16 | } 17 | 18 | @Override 19 | public boolean equals(Object o) { 20 | if (this == o) { 21 | return true; 22 | } 23 | if (o == null || getClass() != o.getClass()) { 24 | return false; 25 | } 26 | MeasurementError that = (MeasurementError) o; 27 | return errorMessage.equals(that.errorMessage); 28 | } 29 | 30 | @Override 31 | public int hashCode() { 32 | return Objects.hash(errorMessage); 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "MeasurementError{" + "errorMessage='" + errorMessage + '\'' + '}'; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /backend/backend/src/test/java/de/aaaaaaah/velcom/backend/restapi/endpoints/utils/EndpointUtilsTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.endpoints.utils; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.junit.jupiter.api.Assertions.assertThrows; 5 | 6 | import de.aaaaaaah.velcom.shared.util.Pair; 7 | import java.util.List; 8 | import org.junit.jupiter.api.Test; 9 | 10 | class EndpointUtilsTest { 11 | 12 | @Test 13 | void parseValidColonSeparatedArgsCorrectly() { 14 | String args = "hello:world:out:there::goodbye:and:farewell"; 15 | List>> parsedArgs = EndpointUtils.parseColonSeparatedArgs(args); 16 | assertThat(parsedArgs) 17 | .isEqualTo( 18 | List.of( 19 | new Pair<>("hello", List.of("world", "out", "there")), 20 | new Pair<>("goodbye", List.of("and", "farewell")))); 21 | } 22 | 23 | @Test 24 | void throwErrorOnInvalidColonSeparatedArgs() { 25 | String args = "the:second:section:has:too:few:elements::see?::the:third:section:is:fine:though"; 26 | assertThrows(ArgumentParseException.class, () -> EndpointUtils.parseColonSeparatedArgs(args)); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/access/archiveaccess/exceptions/TarTransferException.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.access.archiveaccess.exceptions; 2 | 3 | import de.aaaaaaah.velcom.backend.access.taskaccess.entities.Task; 4 | import java.util.Optional; 5 | import javax.annotation.Nullable; 6 | 7 | /** Thrown when something goes wrong during the transfer of a tar file. */ 8 | public class TarTransferException extends Exception { 9 | 10 | @Nullable private final Task task; 11 | 12 | private static String makeMessage(@Nullable Task task) { 13 | if (task == null) { 14 | return "Failed to transfer tar file"; 15 | } else { 16 | return "Failed to transfer tar file for task " + task; 17 | } 18 | } 19 | 20 | public TarTransferException(Throwable t, @Nullable Task task) { 21 | super(makeMessage(task), t); 22 | this.task = task; 23 | } 24 | 25 | public TarTransferException(@Nullable Task task) { 26 | super(makeMessage(task)); 27 | this.task = task; 28 | } 29 | 30 | public TarTransferException() { 31 | this(null); 32 | } 33 | 34 | public Optional getTask() { 35 | return Optional.ofNullable(task); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/data/runcomparison/RunComparison.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.data.runcomparison; 2 | 3 | import de.aaaaaaah.velcom.backend.access.benchmarkaccess.entities.Run; 4 | import de.aaaaaaah.velcom.backend.access.dimensionaccess.entities.Dimension; 5 | import java.util.List; 6 | import java.util.Set; 7 | import java.util.stream.Collectors; 8 | 9 | /** The result of comparing two runs. */ 10 | public class RunComparison { 11 | 12 | private final Run first; 13 | private final Run second; 14 | private final List differences; 15 | 16 | public RunComparison(Run first, Run second, List differences) { 17 | this.first = first; 18 | this.second = second; 19 | this.differences = differences; 20 | } 21 | 22 | public Run getFirst() { 23 | return first; 24 | } 25 | 26 | public Run getSecond() { 27 | return second; 28 | } 29 | 30 | public List getDifferences() { 31 | return differences; 32 | } 33 | 34 | public Set getDimensions() { 35 | return differences.stream().map(DimensionDifference::getDimension).collect(Collectors.toSet()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/access/archiveaccess/exceptions/TarRetrieveException.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.access.archiveaccess.exceptions; 2 | 3 | import de.aaaaaaah.velcom.backend.access.taskaccess.entities.Task; 4 | import java.util.Optional; 5 | import javax.annotation.Nullable; 6 | 7 | /** Thrown when something goes wrong while preparing for the transfer of a tar file. */ 8 | public class TarRetrieveException extends Exception { 9 | 10 | @Nullable private final Task task; 11 | 12 | private static String makeMessage(@Nullable Task task) { 13 | if (task == null) { 14 | return "Failed to retrieve tar file"; 15 | } else { 16 | return "Failed to retrieve tar file for task " + task; 17 | } 18 | } 19 | 20 | public TarRetrieveException(Throwable t, @Nullable Task task) { 21 | super(makeMessage(task), t); 22 | this.task = task; 23 | } 24 | 25 | public TarRetrieveException(@Nullable Task task) { 26 | super(makeMessage(task)); 27 | this.task = task; 28 | } 29 | 30 | public TarRetrieveException() { 31 | this(null); 32 | } 33 | 34 | public Optional getTask() { 35 | return Optional.ofNullable(task); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/access/repoaccess/entities/RemoteUrl.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.access.repoaccess.entities; 2 | 3 | import java.util.Objects; 4 | 5 | /** Locate a remote repository. */ 6 | public class RemoteUrl { 7 | 8 | private final String url; 9 | 10 | /** 11 | * Construct a new remote url. 12 | * 13 | * @param url the url 14 | */ 15 | public RemoteUrl(String url) { 16 | this.url = Objects.requireNonNull(url); 17 | 18 | if (url.isBlank()) { 19 | throw new IllegalArgumentException("url must not be blank: " + url); 20 | } 21 | } 22 | 23 | public String getUrl() { 24 | return url; 25 | } 26 | 27 | @Override 28 | public boolean equals(Object o) { 29 | if (this == o) { 30 | return true; 31 | } 32 | if (o == null || getClass() != o.getClass()) { 33 | return false; 34 | } 35 | RemoteUrl remoteUrl = (RemoteUrl) o; 36 | return url.equals(remoteUrl.url); 37 | } 38 | 39 | @Override 40 | public int hashCode() { 41 | return Objects.hash(url); 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "RemoteUrl{" + "url='" + url + '\'' + '}'; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /backend/backend/src/test/java/de/aaaaaaah/velcom/backend/restapi/jsonobjects/JsonRunDescriptionTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.jsonobjects; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import de.aaaaaaah.velcom.backend.restapi.jsonobjects.JsonRunDescription.JsonSuccess; 5 | import java.util.UUID; 6 | import org.junit.jupiter.api.Test; 7 | 8 | class JsonRunDescriptionTest extends SerializingTest { 9 | 10 | @Test 11 | void serialize() throws JsonProcessingException { 12 | Object object = 13 | new JsonRunDescription( 14 | UUID.fromString("24dd4fd3-5c6d-4542-a7a4-b181f37295a6"), 15 | 1596881630, 16 | JsonSuccess.SUCCESS, 17 | JsonSource.tarSource("descriptionText", null)); 18 | String json = 19 | "{" 20 | + "\"id\": \"24dd4fd3-5c6d-4542-a7a4-b181f37295a6\"," 21 | + "\"start_time\": 1596881630," 22 | + "\"success\": \"SUCCESS\"," 23 | + "\"source\": {" 24 | + " \"type\": \"UPLOADED_TAR\"," 25 | + " \"source\": {\"description\": \"descriptionText\"}" 26 | + "}" 27 | + "}"; 28 | serializedEquals(object, json); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/restapi/exception/NoSuchRepoExceptionMapper.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.exception; 2 | 3 | import de.aaaaaaah.velcom.backend.access.repoaccess.exceptions.NoSuchRepoException; 4 | import java.util.UUID; 5 | import javax.ws.rs.core.Response; 6 | import javax.ws.rs.core.Response.Status; 7 | import javax.ws.rs.ext.ExceptionMapper; 8 | 9 | /** An {@link ExceptionMapper} that transforms {@link NoSuchRepoException}s to NOT_FOUND. */ 10 | public class NoSuchRepoExceptionMapper implements ExceptionMapper { 11 | 12 | @Override 13 | public Response toResponse(NoSuchRepoException exception) { 14 | return Response.status(Status.NOT_FOUND) 15 | .entity(new Info("could not find repo", exception.getInvalidId().getId())) 16 | .build(); 17 | } 18 | 19 | private static class Info { 20 | 21 | private final String message; 22 | private final UUID id; 23 | 24 | public Info(String message, UUID id) { 25 | this.message = message; 26 | this.id = id; 27 | } 28 | 29 | public String getMessage() { 30 | return message; 31 | } 32 | 33 | public UUID getId() { 34 | return id; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/restapi/exception/NoSuchTaskExceptionMapper.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.exception; 2 | 3 | import de.aaaaaaah.velcom.backend.access.taskaccess.exceptions.NoSuchTaskException; 4 | import java.util.UUID; 5 | import javax.ws.rs.core.Response; 6 | import javax.ws.rs.core.Response.Status; 7 | import javax.ws.rs.ext.ExceptionMapper; 8 | 9 | /** An {@link ExceptionMapper} that transforms {@link NoSuchTaskException}s to NOT_FOUND. */ 10 | public class NoSuchTaskExceptionMapper implements ExceptionMapper { 11 | 12 | @Override 13 | public Response toResponse(NoSuchTaskException exception) { 14 | return Response.status(Status.NOT_FOUND) 15 | .entity(new Info("could not find task", exception.getInvalidId().getId())) 16 | .build(); 17 | } 18 | 19 | private static class Info { 20 | 21 | private final String message; 22 | private final UUID id; 23 | 24 | public Info(String message, UUID id) { 25 | this.message = message; 26 | this.id = id; 27 | } 28 | 29 | public String getMessage() { 30 | return message; 31 | } 32 | 33 | public UUID getId() { 34 | return id; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/util/StringOutputStream.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.util; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.OutputStream; 6 | import java.nio.charset.StandardCharsets; 7 | 8 | /** A synchronized {@link OutputStream} that writes to a String. */ 9 | public class StringOutputStream extends OutputStream { 10 | 11 | private final ByteArrayOutputStream byteArrayOutputStream; 12 | 13 | public StringOutputStream() { 14 | this.byteArrayOutputStream = new ByteArrayOutputStream(); 15 | } 16 | 17 | @Override 18 | public synchronized void write(int b) { 19 | byteArrayOutputStream.write(b); 20 | } 21 | 22 | @Override 23 | public synchronized void write(byte[] b) throws IOException { 24 | byteArrayOutputStream.write(b); 25 | } 26 | 27 | @Override 28 | public synchronized void write(byte[] b, int off, int len) { 29 | byteArrayOutputStream.write(b, off, len); 30 | } 31 | 32 | /** 33 | * Returns the underlying read string. 34 | * 35 | * @return the underlying string 36 | */ 37 | public synchronized String getString() { 38 | return byteArrayOutputStream.toString(StandardCharsets.UTF_8); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/access/committaccess/entities/FullCommit.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.access.committaccess.entities; 2 | 3 | import de.aaaaaaah.velcom.backend.access.repoaccess.entities.RepoId; 4 | import java.time.Instant; 5 | import java.util.Set; 6 | 7 | /** A git commit with parent and child hashes. */ 8 | public class FullCommit extends Commit { 9 | 10 | private final Set parentHashes; 11 | private final Set childHashes; 12 | 13 | public FullCommit( 14 | RepoId repoId, 15 | CommitHash hash, 16 | boolean reachable, 17 | boolean tracked, 18 | String author, 19 | Instant authorDate, 20 | String committer, 21 | Instant committerDate, 22 | String message, 23 | Set parentHashes, 24 | Set childHashes) { 25 | 26 | super(repoId, hash, reachable, tracked, author, authorDate, committer, committerDate, message); 27 | 28 | this.parentHashes = parentHashes; 29 | this.childHashes = childHashes; 30 | } 31 | 32 | public Set getParentHashes() { 33 | return parentHashes; 34 | } 35 | 36 | public Set getChildHashes() { 37 | return childHashes; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /backend/aggregator/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | velcom 7 | de.aaaaaaah 8 | 0.0 9 | 10 | 4.0.0 11 | 12 | aggregator 13 | 14 | 15 | 16 | 17 | org.jacoco 18 | jacoco-maven-plugin 19 | 20 | 21 | 22 | 23 | 24 | 25 | de.aaaaaaah 26 | shared 27 | 0.0 28 | 29 | 30 | de.aaaaaaah 31 | runner 32 | 0.0 33 | 34 | 35 | de.aaaaaaah 36 | backend 37 | 0.0 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/access/benchmarkaccess/exceptions/NoSuchRunException.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.access.benchmarkaccess.exceptions; 2 | 3 | import de.aaaaaaah.velcom.backend.access.benchmarkaccess.entities.RunId; 4 | import de.aaaaaaah.velcom.backend.access.committaccess.entities.CommitHash; 5 | import de.aaaaaaah.velcom.backend.access.repoaccess.entities.RepoId; 6 | import de.aaaaaaah.velcom.shared.util.Either; 7 | import de.aaaaaaah.velcom.shared.util.Pair; 8 | 9 | /** This exception is thrown whenever an invalid {@link RunId} is used. */ 10 | public class NoSuchRunException extends RuntimeException { 11 | 12 | private final Either> invalidSource; 13 | 14 | public NoSuchRunException(RunId invalidId) { 15 | super("no run with id " + invalidId); 16 | invalidSource = Either.ofLeft(invalidId); 17 | } 18 | 19 | public NoSuchRunException(RepoId invalidRepoId, CommitHash invalidCommitHash) { 20 | super("no run for commit " + invalidCommitHash + " in repo " + invalidRepoId); 21 | invalidSource = Either.ofRight(new Pair<>(invalidRepoId, invalidCommitHash)); 22 | } 23 | 24 | public Either> getInvalidSource() { 25 | return invalidSource; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /backend/backend/src/test/java/de/aaaaaaah/velcom/backend/restapi/jsonobjects/SerializingTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.jsonobjects; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude.Include; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.JsonNode; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | import com.fasterxml.jackson.databind.PropertyNamingStrategy; 8 | import org.assertj.core.api.Assertions; 9 | import org.junit.jupiter.api.BeforeEach; 10 | 11 | public abstract class SerializingTest { 12 | 13 | protected ObjectMapper objectMapper; 14 | 15 | @BeforeEach 16 | void setUp() { 17 | // This mapper should be configured the same as the one in ServerMain.java 18 | objectMapper = 19 | new ObjectMapper() 20 | .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) 21 | .setSerializationInclusion(Include.NON_NULL); 22 | } 23 | 24 | protected void serializedEquals(Object object, String json) throws JsonProcessingException { 25 | JsonNode objectTree = objectMapper.readTree(objectMapper.writeValueAsString(object)); 26 | JsonNode jsonTree = objectMapper.readTree(json); 27 | Assertions.assertThat(objectTree).isEqualTo(jsonTree); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /backend/checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /frontend/src/util/json/RepoJsonHelper.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ 2 | import { RepoBranch, Repo, Dimension, GithubBotCommand } from "@/store/types"; 3 | 4 | /** 5 | * Parses a repo json to a Repo object. 6 | * 7 | * @export 8 | * @param {*} json the json object 9 | * @returns {Repo} the repo object 10 | */ 11 | export function repoFromJson(json: any): Repo { 12 | let lastGithubUpdate: Date | undefined; 13 | if (json.last_github_update !== undefined) { 14 | lastGithubUpdate = new Date(json.last_github_update * 1000); 15 | } 16 | return new Repo( 17 | json.id, 18 | json.name, 19 | json.branches.map(repoBranchFromJson), 20 | json.dimensions.map((it: any) => dimensionFromJson(it)), 21 | json.remote_url, 22 | lastGithubUpdate, 23 | ); 24 | } 25 | 26 | export function repoBranchFromJson(json: any): RepoBranch { 27 | return new RepoBranch(json.name, json.tracked, json.latest_commit); 28 | } 29 | 30 | export function dimensionFromJson(json: any): Dimension { 31 | return new Dimension(json.benchmark, json.metric, json.unit, json.interpretation); 32 | } 33 | 34 | export function githubCommandFromJson(json: any): GithubBotCommand { 35 | return new GithubBotCommand(json.status, json.comment_id, json.pr_number); 36 | } 37 | -------------------------------------------------------------------------------- /frontend/src/components/misc/TextChip.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 45 | 46 | 54 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/GitProperties.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared; 2 | 3 | import java.io.IOException; 4 | import java.util.Properties; 5 | 6 | /** 7 | * Provides some build information such as the build time, commit hash or version. 8 | * 9 | *

When changing this, also update the sections marked with the comment "Current commit hash 10 | * available to jars" in backend/pom.xml and runner/pom.xml. 11 | */ 12 | public class GitProperties { 13 | 14 | private static final Properties PROPERTIES; 15 | 16 | static { 17 | PROPERTIES = new Properties(); 18 | try { 19 | PROPERTIES.load(GitProperties.class.getResourceAsStream("/git.properties")); 20 | } catch (IOException e) { 21 | throw new RuntimeException("Could not initialize git properties", e); 22 | } 23 | } 24 | 25 | public static String getBuildTime() { 26 | return PROPERTIES.getProperty("git.build.time"); 27 | } 28 | 29 | public static String getVersion() { 30 | return PROPERTIES.getProperty("git.build.version"); 31 | } 32 | 33 | public static String getHash() { 34 | return PROPERTIES.getProperty("git.commit.id.full"); 35 | } 36 | 37 | public static String getHashAbbrev() { 38 | return PROPERTIES.getProperty("git.commit.id.abbrev"); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /frontend/src/util/GraphVariantSelection.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import EchartsDetailGraph from "@/components/graphs/graph/EchartsDetailGraph.vue"; 3 | import DytailGraph from "@/components/graphs/graph/DytailGraph.vue"; 4 | 5 | export type GraphVariant = { 6 | component: typeof Vue; 7 | name: string; 8 | }; 9 | 10 | type GraphVariantPredicate = GraphVariant & { 11 | predicate: (visiblePoints: number) => boolean; 12 | }; 13 | 14 | export const availableGraphComponents: GraphVariantPredicate[] = [ 15 | { 16 | predicate: (visiblePoints: number): boolean => { 17 | // Do not care about zooming, only use echarts when he have only a handful of data points 18 | return visiblePoints < 30_000; 19 | }, 20 | component: EchartsDetailGraph, 21 | name: "Fancy", 22 | }, 23 | { 24 | predicate: (): boolean => { 25 | // matches from first to last. this one is the fallback 26 | return true; 27 | }, 28 | component: DytailGraph, 29 | name: "Fast", 30 | }, 31 | ]; 32 | 33 | /** 34 | * Selets a fitting graph variant. 35 | * 36 | * @param visiblePoints the amount of visible points 37 | */ 38 | export function selectGraphVariant(visiblePoints: number): GraphVariant | undefined { 39 | return availableGraphComponents.find((it) => it.predicate(visiblePoints)); 40 | } 41 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/access/repoaccess/exceptions/FailedToAddRepoException.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.access.repoaccess.exceptions; 2 | 3 | import de.aaaaaaah.velcom.backend.access.repoaccess.entities.RemoteUrl; 4 | import de.aaaaaaah.velcom.shared.util.StringHelper; 5 | 6 | // TODO: 04.10.20 Update the db schema to make repo names unique and update javadoc 7 | // Add this text to the javadoc below once the db schema has been updated: 8 | // This is most likely due to another repo with the same name already existing. 9 | 10 | /** This exception is thrown when a repo could not be added into the db. */ 11 | public class FailedToAddRepoException extends RuntimeException { 12 | 13 | private final String name; 14 | private final RemoteUrl remoteUrl; 15 | 16 | public FailedToAddRepoException(Throwable t, String name, RemoteUrl remoteUrl) { 17 | super( 18 | "failed to add repo named " 19 | + StringHelper.quote(name) 20 | + " for remote url " 21 | + StringHelper.quote(remoteUrl.getUrl()), 22 | t); 23 | 24 | this.name = name; 25 | this.remoteUrl = remoteUrl; 26 | } 27 | 28 | public String getName() { 29 | return name; 30 | } 31 | 32 | public RemoteUrl getRemoteUrl() { 33 | return remoteUrl; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/storage/repo/exception/AddRepositoryException.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.storage.repo.exception; 2 | 3 | import java.util.Objects; 4 | 5 | /** An exception that occurs when trying to add a repository to a repo storage fails. */ 6 | public class AddRepositoryException extends Exception { 7 | 8 | private final String dirName; 9 | private final String remoteUrl; 10 | 11 | /** 12 | * Constructs a new {@link AddRepositoryException}. 13 | * 14 | * @param dirName the directory name for the repository 15 | * @param remoteUrl the remote url of the repository 16 | * @param cause the cause 17 | */ 18 | public AddRepositoryException(String dirName, String remoteUrl, Throwable cause) { 19 | super("failed to add repository to directory \"" + dirName + "\": " + remoteUrl, cause); 20 | this.dirName = Objects.requireNonNull(dirName); 21 | this.remoteUrl = Objects.requireNonNull(remoteUrl); 22 | } 23 | 24 | /** 25 | * @return Returns the directory that should have been the directory for the repository 26 | */ 27 | public String getDirName() { 28 | return dirName; 29 | } 30 | 31 | /** 32 | * @return Returns the remote url of the repository 33 | */ 34 | public String getRemoteUrl() { 35 | return remoteUrl; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/restapi/endpoints/DimensionsEndpoint.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.endpoints; 2 | 3 | import de.aaaaaaah.velcom.backend.access.dimensionaccess.DimensionWriteAccess; 4 | import de.aaaaaaah.velcom.backend.restapi.authentication.Admin; 5 | import de.aaaaaaah.velcom.backend.restapi.jsonobjects.JsonDimensionId; 6 | import io.dropwizard.auth.Auth; 7 | import io.micrometer.core.annotation.Timed; 8 | import java.util.List; 9 | import java.util.stream.Collectors; 10 | import javax.validation.constraints.NotNull; 11 | import javax.ws.rs.DELETE; 12 | import javax.ws.rs.Path; 13 | import javax.ws.rs.Produces; 14 | import javax.ws.rs.core.MediaType; 15 | 16 | /** Endpoint for deleting dimensions. */ 17 | @Path("/dimensions/") 18 | @Produces(MediaType.APPLICATION_JSON) 19 | public class DimensionsEndpoint { 20 | 21 | private final DimensionWriteAccess dimensionAccess; 22 | 23 | public DimensionsEndpoint(DimensionWriteAccess dimensionAccess) { 24 | this.dimensionAccess = dimensionAccess; 25 | } 26 | 27 | @DELETE 28 | @Timed(histogram = true) 29 | public void delete(@Auth Admin admin, @NotNull List request) { 30 | dimensionAccess.deleteDimensions( 31 | request.stream().map(JsonDimensionId::toDimension).collect(Collectors.toList())); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/storage/repo/exception/NoSuchRepositoryException.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.storage.repo.exception; 2 | 3 | import de.aaaaaaah.velcom.backend.storage.repo.RepoStorage; 4 | import java.util.NoSuchElementException; 5 | 6 | /** 7 | * An exception that occurs when trying to access a repository stored in a {@link RepoStorage} which 8 | * does not exist. 9 | */ 10 | public class NoSuchRepositoryException extends NoSuchElementException { 11 | 12 | private final String dirName; 13 | private final RepoStorage storage; 14 | 15 | /** 16 | * Consturcts a new {@link NoSuchRepositoryException}. 17 | * 18 | * @param dirName the directory that is missing a repository 19 | * @param storage the storage 20 | */ 21 | public NoSuchRepositoryException(String dirName, RepoStorage storage) { 22 | super("no repository exists with given name " + dirName + " in storage " + storage); 23 | this.dirName = dirName; 24 | this.storage = storage; 25 | } 26 | 27 | /** 28 | * @return the directory that is missing the repository 29 | */ 30 | public String getDirName() { 31 | return dirName; 32 | } 33 | 34 | /** 35 | * @return the storage where the repository is missing in 36 | */ 37 | public RepoStorage getStorage() { 38 | return storage; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/runner/single/TeleBinaryOutputStream.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.runner.single; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | import java.nio.ByteBuffer; 6 | import org.eclipse.jetty.websocket.api.Session; 7 | 8 | /** A binary output stream streaming data to the runner. */ 9 | class TeleBinaryOutputStream extends OutputStream { 10 | 11 | private static final int DEFAULT_BUFFER_SIZE = 1024; // 1 MB 12 | 13 | private boolean closed; 14 | private ByteBuffer buffer; 15 | private final Session session; 16 | 17 | public TeleBinaryOutputStream(Session session) { 18 | this.session = session; 19 | this.buffer = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE); 20 | } 21 | 22 | @Override 23 | public void write(int b) throws IOException { 24 | if (!buffer.hasRemaining()) { 25 | buffer.rewind(); 26 | session.getRemote().sendPartialBytes(buffer, false); 27 | buffer = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE); 28 | } 29 | buffer.put((byte) b); 30 | } 31 | 32 | @Override 33 | public void close() throws IOException { 34 | if (closed) { 35 | return; 36 | } 37 | buffer.limit(buffer.position()); 38 | buffer.rewind(); 39 | session.getRemote().sendPartialBytes(buffer, true); 40 | closed = true; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /frontend/src/util/DayEquidistantUtils.ts: -------------------------------------------------------------------------------- 1 | import { GraphDataPoint } from "@/store/types"; 2 | 3 | const millisPerDay: number = 1000 * 60 * 60 * 24; 4 | 5 | // https://stackoverflow.com/questions/14446511/most-efficient-method-to-groupby-on-an-array-of-objects 6 | export function groupBy(list: V[], keyGetter: (value: V) => K): Map { 7 | const map = new Map(); 8 | list.forEach((item) => { 9 | const key = keyGetter(item); 10 | const collection = map.get(key); 11 | if (!collection) { 12 | map.set(key, [item]); 13 | } else { 14 | collection.push(item); 15 | } 16 | }); 17 | return map; 18 | } 19 | 20 | export function spaceDayEquidistant(detailGraph: T[]): T[] { 21 | const dayGroups: Map = groupBy( 22 | detailGraph, 23 | (key) => 24 | // round to day 25 | Math.floor(key.committerTime.getTime() / millisPerDay) * millisPerDay, 26 | ); 27 | 28 | const groupEntries = Array.from(dayGroups.entries()); 29 | groupEntries.sort((a, b) => a[0] - b[0]); 30 | 31 | return groupEntries.flatMap(([day, points]) => { 32 | const spacingBetweenElementsMillis = millisPerDay / points.length; 33 | 34 | return points.map((point, index) => { 35 | return point.positionedAt(new Date(day + spacingBetweenElementsMillis * index)) as T; 36 | }); 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /backend/backend/src/main/resources/db/migration/V12__commit_tracking_flags.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE known_commit RENAME TO known_commit_old; 2 | 3 | CREATE TABLE known_commit ( 4 | repo_id CHAR(36) NOT NULL, 5 | hash CHAR(40) NOT NULL, 6 | reachable BOOLEAN NOT NULL, -- Whether this commit is reachable from any branch 7 | tracked BOOLEAN NOT NULL, -- Whether this commit is reachable from a tracked branch 8 | ever_tracked BOOLEAN NOT NULL, -- Whether this commit has ever been tracked 9 | author TEXT NOT NULL, 10 | author_date TIMESTAMP NOT NULL, 11 | committer TEXT NOT NULL, 12 | committer_date TIMESTAMP NOT NULL, 13 | message TEXT NOT NULL, 14 | 15 | -- "tracked" implies "ever_tracked" 16 | CHECK (NOT tracked OR ever_tracked), 17 | 18 | PRIMARY KEY (repo_id, hash), 19 | FOREIGN KEY (repo_id) REFERENCES repo (id) ON UPDATE CASCADE ON DELETE CASCADE 20 | ); 21 | 22 | INSERT INTO known_commit 23 | SELECT 24 | repo_id, 25 | hash, 26 | reachable, 27 | tracked, 28 | tracked, 29 | author, 30 | author_date, 31 | committer, 32 | committer_date, 33 | message 34 | FROM known_commit_old; 35 | 36 | DROP TABLE known_commit_old; 37 | 38 | -- Recapture foreign key references 39 | ALTER TABLE known_commit RENAME TO known_commit_old; 40 | ALTER TABLE known_commit_old RENAME TO known_commit; 41 | -------------------------------------------------------------------------------- /backend/runner/src/main/java/de/aaaaaaah/velcom/runner/Delays.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.runner; 2 | 3 | import java.time.Duration; 4 | 5 | /** A class for keeping all kinds of runner-related delays and timeouts. */ 6 | public final class Delays { 7 | 8 | /** 9 | * How long to wait for a reconnect when creating a connection fails. This delay prevents flooding 10 | * the server on the other end with too many connection attempts. 11 | */ 12 | public static final Duration RECONNECT_AFTER_FAILED_CONNECTION = Duration.ofSeconds(10); 13 | 14 | /** 15 | * How long to wait for a server response when trying to close a connection before force-closing 16 | * it. 17 | */ 18 | public static final Duration CLOSE_CONNECTION_TIMEOUT = Duration.ofSeconds(10); 19 | 20 | /** How long to pause in-between rounds of asking the backends for new benchmarks. */ 21 | public static final Duration BACKEND_ROUNDTRIP = Duration.ofSeconds(10); 22 | 23 | /** How long to wait - after sending a command to the server - for a reply to that command. */ 24 | public static final Duration AWAIT_COMMAND_REPLY = Duration.ofSeconds(10); 25 | 26 | /** How long to wait until the sigkilling begins (in the Benchmarker). */ 27 | public static final Duration TIME_TO_KILL = Duration.ofSeconds(10); 28 | 29 | private Delays() { 30 | throw new UnsupportedOperationException(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/storage/repo/exception/RepositoryAcquisitionException.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.storage.repo.exception; 2 | 3 | import de.aaaaaaah.velcom.backend.storage.repo.RepoStorage; 4 | 5 | /** An exception that occurs when the acquisition of a local repository has failed. */ 6 | public class RepositoryAcquisitionException extends Exception { 7 | 8 | private final RepoStorage repoStorage; 9 | private final String dirName; 10 | 11 | /** 12 | * Constructs a new {@link RepositoryAcquisitionException}. 13 | * 14 | * @param repoStorage the storage wherein the acquisition was attempted 15 | * @param dirName the directory name of the repository to acquire 16 | * @param e the cause for this exception 17 | */ 18 | public RepositoryAcquisitionException(RepoStorage repoStorage, String dirName, Exception e) { 19 | super("Failed to acquire repository " + dirName + " in storage " + repoStorage, e); 20 | this.repoStorage = repoStorage; 21 | this.dirName = dirName; 22 | } 23 | 24 | /** 25 | * @return the storage wherein the acquisition was attempted 26 | */ 27 | public RepoStorage getRepoStorage() { 28 | return repoStorage; 29 | } 30 | 31 | /** 32 | * @return the directory name of the repository to acquire 33 | */ 34 | public String getDirName() { 35 | return dirName; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/util/Pair.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.util; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * A simple key-value pair. 7 | * 8 | *

This pair has a consistent {@link #equals(Object)} and {@link #hashCode()} method, if the two 9 | * element types have one. 10 | * 11 | * @param the type of the first element in the pair 12 | * @param the type of the second element in the pair 13 | */ 14 | public class Pair { 15 | 16 | private final A first; 17 | private final B second; 18 | 19 | public Pair(A first, B second) { 20 | this.first = first; 21 | this.second = second; 22 | } 23 | 24 | public A getFirst() { 25 | return first; 26 | } 27 | 28 | public B getSecond() { 29 | return second; 30 | } 31 | 32 | @Override 33 | public boolean equals(Object o) { 34 | if (this == o) { 35 | return true; 36 | } 37 | if (o == null || getClass() != o.getClass()) { 38 | return false; 39 | } 40 | Pair pair = (Pair) o; 41 | return Objects.equals(first, pair.first) && Objects.equals(second, pair.second); 42 | } 43 | 44 | @Override 45 | public int hashCode() { 46 | return Objects.hash(first, second); 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | return "Pair{" + "first=" + first + ", second=" + second + '}'; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /backend/shared/src/test/java/de/aaaaaaah/velcom/shared/util/ExceptionHelperTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.util; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class ExceptionHelperTest { 8 | 9 | @Test 10 | void containsThisClass() { 11 | String stackTrace = ExceptionHelper.getStackTrace(new Throwable()); 12 | assertThat(stackTrace).contains("ExceptionHelperTest").contains("containsThisClass"); 13 | } 14 | 15 | @Test 16 | void containsNestedStacktrace() { 17 | RuntimeException inner = new RuntimeException("hello there"); 18 | RuntimeException outer = new RuntimeException(inner); 19 | 20 | String stackTrace = ExceptionHelper.getStackTrace(outer); 21 | assertThat(stackTrace) 22 | .contains("ExceptionHelperTest") 23 | .contains("containsNestedStacktrace") 24 | .contains("hello there"); 25 | } 26 | 27 | @Test 28 | void containsSuppressedStacktrace() { 29 | RuntimeException inner = new RuntimeException("hello there"); 30 | RuntimeException outer = new RuntimeException(); 31 | outer.addSuppressed(inner); 32 | 33 | String stackTrace = ExceptionHelper.getStackTrace(outer); 34 | assertThat(stackTrace) 35 | .contains("ExceptionHelperTest") 36 | .contains("containsSuppressedStacktrace") 37 | .contains("hello there"); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/data/significance/SignificanceFactors.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.data.significance; 2 | 3 | /** 4 | * A few factors describing how the standard deviation and significance of a run should be 5 | * determined. 6 | */ 7 | public class SignificanceFactors { 8 | 9 | private final double relativeThreshold; 10 | private final double stddevThreshold; 11 | private final int minStddevAmount; 12 | 13 | public SignificanceFactors( 14 | double relativeThreshold, double stddevThreshold, int minStddevAmount) { 15 | 16 | if (relativeThreshold < 0) { 17 | throw new IllegalArgumentException("relative threshold must be >= 0"); 18 | } 19 | if (stddevThreshold < 0) { 20 | throw new IllegalArgumentException("stddev threshold must be >= 0"); 21 | } 22 | if (minStddevAmount < 2) { 23 | throw new IllegalArgumentException("minimum stddev amount must be at least 2"); 24 | } 25 | 26 | this.relativeThreshold = relativeThreshold; 27 | this.stddevThreshold = stddevThreshold; 28 | this.minStddevAmount = minStddevAmount; 29 | } 30 | 31 | public double getRelativeThreshold() { 32 | return relativeThreshold; 33 | } 34 | 35 | public double getStddevThreshold() { 36 | return stddevThreshold; 37 | } 38 | 39 | public int getMinStddevAmount() { 40 | return minStddevAmount; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/protocol/serialization/clientbound/ClientBoundPacket.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.serialization.clientbound; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.fasterxml.jackson.databind.JsonNode; 6 | import java.util.Objects; 7 | 8 | /** A packet that can be sent to the runner by the backend. */ 9 | public class ClientBoundPacket { 10 | 11 | private final ClientBoundPacketType type; 12 | private final JsonNode data; 13 | 14 | @JsonCreator 15 | public ClientBoundPacket( 16 | @JsonProperty(required = true) ClientBoundPacketType type, 17 | @JsonProperty(required = true) JsonNode data) { 18 | this.type = type; 19 | this.data = data; 20 | } 21 | 22 | public ClientBoundPacketType getType() { 23 | return type; 24 | } 25 | 26 | public JsonNode getData() { 27 | return data; 28 | } 29 | 30 | @Override 31 | public boolean equals(Object o) { 32 | if (this == o) { 33 | return true; 34 | } 35 | if (o == null || getClass() != o.getClass()) { 36 | return false; 37 | } 38 | ClientBoundPacket that = (ClientBoundPacket) o; 39 | return type == that.type && data.equals(that.data); 40 | } 41 | 42 | @Override 43 | public int hashCode() { 44 | return Objects.hash(type, data); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /backend/backend/src/test/java/de/aaaaaaah/velcom/backend/access/dimensionaccess/entities/DimensionTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.access.dimensionaccess.entities; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class DimensionTest { 8 | 9 | @SuppressWarnings("EqualsWithItself") 10 | @Test 11 | void compareDimensions() { 12 | Dimension dimAA = new Dimension("a", "a"); 13 | Dimension dimAB = new Dimension("a", "b"); 14 | Dimension dimBA = new Dimension("b", "a"); 15 | Dimension dimBB = new Dimension("b", "b"); 16 | 17 | assertThat(dimAA.compareTo(dimAA)).isEqualTo(0); 18 | assertThat(dimAA).isLessThan(dimAB); 19 | assertThat(dimAA).isLessThan(dimBA); 20 | assertThat(dimAA).isLessThan(dimBB); 21 | 22 | assertThat(dimAB).isGreaterThan(dimAA); 23 | assertThat(dimAB.compareTo(dimAB)).isEqualTo(0); 24 | assertThat(dimAB).isLessThan(dimBA); 25 | assertThat(dimAB).isLessThan(dimBB); 26 | 27 | assertThat(dimBA).isGreaterThan(dimAA); 28 | assertThat(dimBA).isGreaterThan(dimAB); 29 | assertThat(dimBA.compareTo(dimBA)).isEqualTo(0); 30 | assertThat(dimBA).isLessThan(dimBB); 31 | 32 | assertThat(dimBB).isGreaterThan(dimAA); 33 | assertThat(dimBB).isGreaterThan(dimAB); 34 | assertThat(dimBB).isGreaterThan(dimBA); 35 | assertThat(dimBB.compareTo(dimBB)).isEqualTo(0); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | VelCom 9 | 10 | 20 | 21 | 22 |

24 |        .----------.      ____________ ‎
25 |       /  .-.  .-.  \    /            \‎
26 |      /   | |  | |   \   |  Aaaaaaah! |‎
27 |      \   `-'  `-'  _/   / ___________/‎
28 |      /\     .--.  / |  /_/            ‎
29 |      \ |   /  /  / /                  ‎
30 |      / |   `--' /\ \                  ‎
31 |     / / `-------' \ \      Jym Dyer   ‎
32 | 
33 | 
34 | We're sorry but VelCom doesn't work
35 | properly without JavaScript.
36 | Please enable JavaScript to continue.
37 |       
38 | 39 |
40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/access/benchmarkaccess/entities/RunError.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.access.benchmarkaccess.entities; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * Occurs when either the benchmark script cause a global error or the runner itself fails to 7 | * execute the benchmark script. 8 | */ 9 | public class RunError { 10 | 11 | private final String errorMessage; 12 | private final RunErrorType type; 13 | 14 | public RunError(String errorMessage, RunErrorType type) { 15 | this.errorMessage = Objects.requireNonNull(errorMessage); 16 | this.type = Objects.requireNonNull(type); 17 | } 18 | 19 | public String getMessage() { 20 | return errorMessage; 21 | } 22 | 23 | public RunErrorType getType() { 24 | return type; 25 | } 26 | 27 | @Override 28 | public boolean equals(Object o) { 29 | if (this == o) { 30 | return true; 31 | } 32 | if (o == null || getClass() != o.getClass()) { 33 | return false; 34 | } 35 | RunError runError = (RunError) o; 36 | return Objects.equals(errorMessage, runError.errorMessage) && type == runError.type; 37 | } 38 | 39 | @Override 40 | public int hashCode() { 41 | return Objects.hash(errorMessage, type); 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "RunError{" + "errorMessage='" + errorMessage + '\'' + ", type=" + type + '}'; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /backend/shared/src/test/java/de/aaaaaaah/velcom/shared/util/systeminfo/LinuxSystemInfoTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.util.systeminfo; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.junit.jupiter.api.Assumptions.assumeFalse; 5 | 6 | import de.aaaaaaah.velcom.shared.util.OSCheck; 7 | import org.junit.jupiter.api.BeforeEach; 8 | import org.junit.jupiter.api.Test; 9 | 10 | class LinuxSystemInfoTest { 11 | 12 | private LinuxSystemInfo systemInfo; 13 | 14 | @BeforeEach 15 | void setUp() { 16 | systemInfo = LinuxSystemInfo.getCurrent(); 17 | } 18 | 19 | @Test 20 | void hasPlausibleCpuInfo() { 21 | assumeFalse(OSCheck.isStupidWindows()); 22 | 23 | assertThat(systemInfo.getCpuInfo().cpuCount()).isGreaterThan(0); 24 | assertThat(systemInfo.getCpuInfo().physicalCoreCount()).isGreaterThan(0); 25 | assertThat(systemInfo.getCpuInfo().coreModels()).hasSizeGreaterThanOrEqualTo(1); 26 | 27 | assertThat(systemInfo.getCpuInfo().virtualCoreCount()) 28 | .isGreaterThanOrEqualTo(Runtime.getRuntime().availableProcessors()); 29 | } 30 | 31 | @Test 32 | void hasPlausibleMemoryInfo() { 33 | assumeFalse(OSCheck.isStupidWindows()); 34 | 35 | assertThat(systemInfo.getMemoryInfo().freeMemoryKiB()).isGreaterThanOrEqualTo(0); 36 | assertThat(systemInfo.getMemoryInfo().totalMemoryKib()) 37 | .isGreaterThanOrEqualTo(Runtime.getRuntime().maxMemory() / 1024); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/restapi/jsonobjects/JsonRepo.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.jsonobjects; 2 | 3 | import java.util.List; 4 | import java.util.UUID; 5 | import javax.annotation.Nullable; 6 | 7 | public class JsonRepo { 8 | 9 | private final UUID id; 10 | private final String name; 11 | private final String remoteUrl; 12 | private final List branches; 13 | private final List dimensions; 14 | @Nullable private final Long lastGithubUpdate; 15 | 16 | public JsonRepo( 17 | UUID id, 18 | String name, 19 | String remoteUrl, 20 | List branches, 21 | List dimensions, 22 | @Nullable Long lastGithubUpdate) { 23 | 24 | this.id = id; 25 | this.name = name; 26 | this.remoteUrl = remoteUrl; 27 | this.branches = branches; 28 | this.dimensions = dimensions; 29 | this.lastGithubUpdate = lastGithubUpdate; 30 | } 31 | 32 | public UUID getId() { 33 | return id; 34 | } 35 | 36 | public String getName() { 37 | return name; 38 | } 39 | 40 | public String getRemoteUrl() { 41 | return remoteUrl; 42 | } 43 | 44 | public List getBranches() { 45 | return branches; 46 | } 47 | 48 | public List getDimensions() { 49 | return dimensions; 50 | } 51 | 52 | @Nullable 53 | public Long getLastGithubUpdate() { 54 | return lastGithubUpdate; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /backend/backend/src/test/java/de/aaaaaaah/velcom/backend/restapi/jsonobjects/JsonRunTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.jsonobjects; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import java.util.UUID; 5 | import org.junit.jupiter.api.Test; 6 | 7 | class JsonRunTest extends SerializingTest { 8 | 9 | @Test 10 | void serialize() throws JsonProcessingException { 11 | Object object = 12 | new JsonRun( 13 | UUID.fromString("24dd4fd3-5c6d-4542-a7a4-b181f37295a6"), 14 | "authorName", 15 | "runnerName", 16 | "runnerInfoText", 17 | 1596881630, 18 | 1596881676, 19 | JsonSource.tarSource("descriptionText", null), 20 | JsonResult.velcomError("velcomErrorText")); 21 | String json = 22 | "{" 23 | + "\"id\": \"24dd4fd3-5c6d-4542-a7a4-b181f37295a6\"," 24 | + "\"author\": \"authorName\"," 25 | + "\"runner_name\": \"runnerName\"," 26 | + "\"runner_info\": \"runnerInfoText\"," 27 | + "\"start_time\": 1596881630," 28 | + "\"stop_time\": 1596881676," 29 | + "\"source\": {" 30 | + " \"type\": \"UPLOADED_TAR\"," 31 | + " \"source\": {\"description\": \"descriptionText\"}" 32 | + "}," 33 | + "\"result\": {\"velcom_error\": \"velcomErrorText\"}" 34 | + "}"; 35 | serializedEquals(object, json); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/runner/single/state/AwaitAbortRunReply.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.runner.single.state; 2 | 3 | import de.aaaaaaah.velcom.backend.runner.single.RunnerConnection; 4 | import de.aaaaaaah.velcom.backend.runner.single.TeleRunner; 5 | import de.aaaaaaah.velcom.shared.protocol.serialization.serverbound.AbortRunReply; 6 | import de.aaaaaaah.velcom.shared.protocol.serialization.serverbound.ServerBoundPacket; 7 | import de.aaaaaaah.velcom.shared.protocol.serialization.serverbound.ServerBoundPacketType; 8 | import java.util.Optional; 9 | 10 | /** A state waiting for a reply to the GetResult message. */ 11 | public class AwaitAbortRunReply extends TimeoutState { 12 | 13 | public AwaitAbortRunReply(TeleRunner runner, RunnerConnection connection) { 14 | super(runner, connection); 15 | } 16 | 17 | @Override 18 | protected Optional onPacket(ServerBoundPacket packet) { 19 | return super.onPacket(packet) 20 | .or( 21 | () -> 22 | Optional.of(packet) 23 | .filter(it -> it.getType() == ServerBoundPacketType.ABORT_RUN_REPLY) 24 | .flatMap( 25 | it -> 26 | connection 27 | .getSerializer() 28 | .deserialize(it.getData(), AbortRunReply.class)) 29 | .map(reply -> new IdleState(runner, connection))); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /backend/runner/src/main/java/de/aaaaaaah/velcom/runner/RunnerCliSpec.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.runner; 2 | 3 | import java.nio.file.Files; 4 | import java.nio.file.Path; 5 | import java.nio.file.Paths; 6 | import java.util.function.Function; 7 | import net.jbock.Command; 8 | import net.jbock.Param; 9 | 10 | /** The runner for VelCom that executes benchmarks. Made by Aaaaaaah! */ 11 | @Command(value = "VelCom-Runner") 12 | public abstract class RunnerCliSpec { 13 | 14 | /** The path to the config file. */ 15 | @Param(value = 1, mappedBy = ConfigFilePathMapper.class) 16 | public abstract Path configFileLocation(); 17 | 18 | /** A mapper for the config file location. */ 19 | public static class ConfigFilePathMapper implements Function { 20 | 21 | @Override 22 | public Path apply(String name) { 23 | Path path = Paths.get(name); 24 | if (Files.notExists(path)) { 25 | throw new IllegalArgumentException( 26 | "The given path (" + path.toAbsolutePath() + ") does not exist!"); 27 | } 28 | if (!Files.isRegularFile(path)) { 29 | throw new IllegalArgumentException( 30 | "The given path (" + path.toAbsolutePath() + ") is no regular file!"); 31 | } 32 | if (!Files.isReadable(path)) { 33 | throw new IllegalArgumentException( 34 | "The given path (" + path.toAbsolutePath() + ") is not readable by me!"); 35 | } 36 | return path.toAbsolutePath(); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/restapi/exception/TaskAlreadyExistsExceptionMapper.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.exception; 2 | 3 | import java.util.UUID; 4 | import javax.ws.rs.core.Response; 5 | import javax.ws.rs.core.Response.Status; 6 | import javax.ws.rs.ext.ExceptionMapper; 7 | 8 | /** An {@link ExceptionMapper} that transforms {@link TaskAlreadyExistsException}s to CONFLICT. */ 9 | public class TaskAlreadyExistsExceptionMapper 10 | implements ExceptionMapper { 11 | 12 | @Override 13 | public Response toResponse(TaskAlreadyExistsException exception) { 14 | return Response.status(Status.CONFLICT) 15 | .entity( 16 | new Info( 17 | "that task already exists in the queue", 18 | exception.getRepoId().getId(), 19 | exception.getHash().getHash())) 20 | .build(); 21 | } 22 | 23 | private static class Info { 24 | 25 | private final String message; 26 | private final UUID repoId; 27 | private final String hash; 28 | 29 | public Info(String message, UUID repoId, String hash) { 30 | this.message = message; 31 | this.repoId = repoId; 32 | this.hash = hash; 33 | } 34 | 35 | public String getMessage() { 36 | return message; 37 | } 38 | 39 | public UUID getRepoId() { 40 | return repoId; 41 | } 42 | 43 | public String getHash() { 44 | return hash; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /cli/velcom_cli/__init__.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import configparser 3 | from pathlib import Path 4 | 5 | from . import commands 6 | from .config import Config 7 | 8 | 9 | def make_parser(): 10 | parser = argparse.ArgumentParser() 11 | parser.add_argument( 12 | "--config", "-c", 13 | type=Path, 14 | metavar="CONFIGFILE", 15 | help="load config from CONFIGFILE instead of the default" 16 | " paths (~/.config/velcom/velcom.conf, ~/.velcom.conf)", 17 | ) 18 | parser.add_argument( 19 | "--profile", "-p", 20 | metavar="PROFILE", 21 | help="name of the config file section to use", 22 | ) 23 | 24 | subparsers = parser.add_subparsers( 25 | required=True, 26 | # A metavar or dest is necessary for "required=True" to not crash when 27 | # arguments contain no command. See https://bugs.python.org/issue29298 28 | metavar="COMMAND", 29 | help="one of the following commands:", 30 | ) 31 | 32 | commands.bench_dir.register(subparsers) 33 | commands.default_config.register(subparsers) 34 | commands.list_repos.register(subparsers) 35 | commands.print_config.register(subparsers) 36 | 37 | return parser 38 | 39 | 40 | def main(): 41 | args = make_parser().parse_args() 42 | config = Config.load(args) 43 | try: 44 | args.f(config) 45 | except configparser.Error as e: 46 | print("Configuration error:") 47 | print(e) 48 | exit(1) 49 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/restapi/jsonobjects/JsonTask.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.jsonobjects; 2 | 3 | import de.aaaaaaah.velcom.backend.access.committaccess.CommitReadAccess; 4 | import de.aaaaaaah.velcom.backend.access.taskaccess.entities.Task; 5 | import java.util.UUID; 6 | 7 | public class JsonTask { 8 | 9 | private final UUID id; 10 | private final String author; 11 | private final long since; 12 | private final JsonSource source; 13 | 14 | public JsonTask(UUID id, String author, long since, JsonSource source) { 15 | this.id = id; 16 | this.author = author; 17 | this.since = since; 18 | this.source = source; 19 | } 20 | 21 | public static JsonTask fromTask(Task task, JsonSource source) { 22 | return new JsonTask( 23 | task.getId().getId(), task.getAuthor(), task.getInsertTime().getEpochSecond(), source); 24 | } 25 | 26 | public static JsonTask fromTask(Task task, CommitReadAccess commitAccess) { 27 | return fromTask( 28 | task, 29 | task.getSource() 30 | .mapLeft(it -> commitAccess.getCommit(it.getRepoId(), it.getHash())) 31 | .consume(JsonSource::fromCommit, JsonSource::fromTarSource)); 32 | } 33 | 34 | public UUID getId() { 35 | return id; 36 | } 37 | 38 | public String getAuthor() { 39 | return author; 40 | } 41 | 42 | public long getSince() { 43 | return since; 44 | } 45 | 46 | public JsonSource getSource() { 47 | return source; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint --no-fix", 9 | "fmt": "prettier . --write", 10 | "fmtchk": "prettier . --check" 11 | }, 12 | "dependencies": { 13 | "@types/dygraphs": "^1.1.11", 14 | "ansi_up": "^5.1.0", 15 | "axios": "^1.6.0", 16 | "core-js": "^3.29.1", 17 | "dygraphs": "I-Al-Istannen/dygraphs#1e0170e98b5e8501d0ab12d897e14b1b322bd169", 18 | "vue": "^2.6.14", 19 | "echarts": "^5.4.1", 20 | "vue-class-component": "^7.2.6", 21 | "vue-echarts": "^6.5.4", 22 | "vue-property-decorator": "^9.1.2", 23 | "vue-router": "^3.5.2", 24 | "vuetify": "^2.6.10", 25 | "vuex": "^3.6.2", 26 | "vuex-class-component": "^2.3.6", 27 | "vuex-persist": "^2.2.0" 28 | }, 29 | "devDependencies": { 30 | "@mdi/js": "^7.1.96", 31 | "@types/echarts": "^4.9.16", 32 | "@vue/cli-plugin-babel": "^5.0.8", 33 | "@vue/cli-plugin-router": "^5.0.8", 34 | "@vue/cli-plugin-typescript": "^5.0.8", 35 | "@vue/cli-plugin-vuex": "^5.0.8", 36 | "@vue/cli-service": "5.0.8", 37 | "@vue/composition-api": "^1.7.1", 38 | "prettier": "^3.6.2", 39 | "sass": "~1.59.3", 40 | "sass-loader": "^13.2.0", 41 | "typescript": "~5.0.2", 42 | "vue-cli-plugin-vuetify": "~2.5.8", 43 | "vue-template-compiler": "^2.7.14", 44 | "vuetify-loader": "^1.9.2" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /frontend/src/util/json/QueueJsonHelper.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ 2 | import { 3 | Task, 4 | TaskSource, 5 | TarTaskSource, 6 | CommitTaskSource, 7 | CommitDescription, 8 | Worker, 9 | StreamedRunnerOutput, 10 | } from "@/store/types"; 11 | 12 | export function workerFromJson(json: any): Worker { 13 | return new Worker( 14 | json.name, 15 | json.info, 16 | json.working_on, 17 | json.working_since ? new Date(json.working_since * 1000) : null, 18 | json.lost_connection, 19 | ); 20 | } 21 | 22 | export function taskFromJson(json: any): Task { 23 | return new Task(json.id, json.author, new Date(json.since * 1000), sourceFromJson(json.source)); 24 | } 25 | 26 | export function sourceFromJson(json: any): TaskSource { 27 | if (json.type === "COMMIT") { 28 | return new CommitTaskSource(commitDescriptionFromJson(json.source)); 29 | } else if (json.type === "UPLOADED_TAR") { 30 | return new TarTaskSource(json.source.description, json.source.repo_id); 31 | } 32 | throw new Error("Unknown task type"); 33 | } 34 | 35 | export function commitDescriptionFromJson(json: any): CommitDescription { 36 | return new CommitDescription( 37 | json.repo_id, 38 | json.hash, 39 | json.author, 40 | new Date(json.author_date * 1000), 41 | json.summary, 42 | ); 43 | } 44 | 45 | export function streamedRunnerOutputFromJson(json: any): StreamedRunnerOutput { 46 | return new StreamedRunnerOutput(json.output, json.index_of_first_line); 47 | } 48 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/restapi/exception/NoSuchCommitExceptionMapper.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.exception; 2 | 3 | import de.aaaaaaah.velcom.backend.access.committaccess.exceptions.NoSuchCommitException; 4 | import java.util.UUID; 5 | import javax.ws.rs.core.Response; 6 | import javax.ws.rs.core.Response.Status; 7 | import javax.ws.rs.ext.ExceptionMapper; 8 | 9 | /** An {@link ExceptionMapper} that transforms {@link NoSuchCommitException}s to NOT_FOUND. */ 10 | public class NoSuchCommitExceptionMapper implements ExceptionMapper { 11 | 12 | @Override 13 | public Response toResponse(NoSuchCommitException exception) { 14 | return Response.status(Status.NOT_FOUND) 15 | .entity( 16 | new Info( 17 | "could not find commit", 18 | exception.getRepoId().getId(), 19 | exception.getCommitHash().getHash())) 20 | .build(); 21 | } 22 | 23 | private static class Info { 24 | 25 | private final String message; 26 | private final UUID repoId; 27 | private final String hash; 28 | 29 | public Info(String message, UUID repoId, String hash) { 30 | this.message = message; 31 | this.repoId = repoId; 32 | this.hash = hash; 33 | } 34 | 35 | public String getMessage() { 36 | return message; 37 | } 38 | 39 | public UUID getRepoId() { 40 | return repoId; 41 | } 42 | 43 | public String getHash() { 44 | return hash; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/KnownHostsIgnoringSshdFactory.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend; 2 | 3 | import java.io.File; 4 | import java.net.InetSocketAddress; 5 | import java.security.PublicKey; 6 | import java.util.Collections; 7 | import java.util.List; 8 | import org.eclipse.jgit.transport.CredentialsProvider; 9 | import org.eclipse.jgit.transport.sshd.KeyCache; 10 | import org.eclipse.jgit.transport.sshd.ProxyDataFactory; 11 | import org.eclipse.jgit.transport.sshd.ServerKeyDatabase; 12 | import org.eclipse.jgit.transport.sshd.SshdSessionFactory; 13 | 14 | /** A {@link SshdSessionFactory} that ignores server keys. */ 15 | public class KnownHostsIgnoringSshdFactory extends SshdSessionFactory { 16 | 17 | public KnownHostsIgnoringSshdFactory(KeyCache keyCache, ProxyDataFactory proxies) { 18 | super(keyCache, proxies); 19 | } 20 | 21 | @Override 22 | protected ServerKeyDatabase getServerKeyDatabase(File homeDir, File sshDir) { 23 | return new ServerKeyDatabase() { 24 | @Override 25 | public List lookup( 26 | String connectAddress, InetSocketAddress remoteAddress, Configuration config) { 27 | return Collections.emptyList(); 28 | } 29 | 30 | @Override 31 | public boolean accept( 32 | String connectAddress, 33 | InetSocketAddress remoteAddress, 34 | PublicKey serverKey, 35 | Configuration config, 36 | CredentialsProvider provider) { 37 | return true; 38 | } 39 | }; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/protocol/serialization/serverbound/ServerBoundPacket.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.serialization.serverbound; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.fasterxml.jackson.databind.JsonNode; 6 | import java.util.Objects; 7 | 8 | /** A packet that can be sent to the backend by the runner. */ 9 | public class ServerBoundPacket { 10 | 11 | private final ServerBoundPacketType type; 12 | private final JsonNode data; 13 | 14 | @JsonCreator 15 | public ServerBoundPacket( 16 | @JsonProperty(required = true) ServerBoundPacketType type, 17 | @JsonProperty(required = true) JsonNode data) { 18 | this.type = type; 19 | this.data = data; 20 | } 21 | 22 | public ServerBoundPacketType getType() { 23 | return type; 24 | } 25 | 26 | public JsonNode getData() { 27 | return data; 28 | } 29 | 30 | @Override 31 | public boolean equals(Object o) { 32 | if (this == o) { 33 | return true; 34 | } 35 | if (o == null || getClass() != o.getClass()) { 36 | return false; 37 | } 38 | ServerBoundPacket that = (ServerBoundPacket) o; 39 | return type == that.type && data.equals(that.data); 40 | } 41 | 42 | @Override 43 | public int hashCode() { 44 | return Objects.hash(type, data); 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return "ServerBoundPacket{" + "type=" + type + ", data=" + data + '}'; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /frontend/src/util/json/RunSearchJsonHelper.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ 2 | import { SearchItem, SearchItemBranch, SearchItemCommit, SearchItemRun } from "@/store/types"; 3 | 4 | function searchItemCommitFromJson(json: any): SearchItemCommit { 5 | return new SearchItemCommit( 6 | json.repo_id, 7 | json.hash, 8 | json.author, 9 | new Date(json.author_date * 1000), 10 | json.committer, 11 | new Date(json.committer_date * 1000), 12 | json.summary, 13 | json.has_run, 14 | ); 15 | } 16 | 17 | function searchItemRunFromJson(json: any): SearchItemRun { 18 | return new SearchItemRun( 19 | json.id, 20 | json.repo_id, 21 | json.commit_hash, 22 | json.commit_summary, 23 | json.tar_description, 24 | new Date(json.start_time * 1000), 25 | new Date(json.stop_time * 1000), 26 | ); 27 | } 28 | 29 | function searchItemBranchFromJson(json: any): SearchItemBranch { 30 | return new SearchItemBranch( 31 | json.repo_id, 32 | json.name, 33 | json.commit_hash, 34 | json.commit_summary, 35 | json.has_run, 36 | ); 37 | } 38 | 39 | /** 40 | * Converts the result of the search endpoint to a list of search search items. 41 | * @param json the json response 42 | * @return the extracted search items 43 | */ 44 | export function searchItemsFromJson(json: any): SearchItem[] { 45 | return json.branches 46 | .map(searchItemBranchFromJson) 47 | .concat(json.commits.map(searchItemCommitFromJson)) 48 | .concat(json.runs.map(searchItemRunFromJson)); 49 | } 50 | -------------------------------------------------------------------------------- /backend/shared/src/test/java/de/aaaaaaah/velcom/shared/protocol/serialization/clientbound/RequestRunReplyTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.serialization.clientbound; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertTrue; 5 | 6 | import com.fasterxml.jackson.core.JsonProcessingException; 7 | import de.aaaaaaah.velcom.shared.protocol.serialization.SerializerBasedTest; 8 | import java.util.Optional; 9 | import java.util.UUID; 10 | import org.junit.jupiter.api.Test; 11 | 12 | class RequestRunReplyTest extends SerializerBasedTest { 13 | 14 | @Test 15 | void deserializeWithNoOptionals() throws JsonProcessingException { 16 | String json = "{\"bench\": false, \"run\": false}"; 17 | Optional result = serializer.deserialize(json, RequestRunReply.class); 18 | 19 | assertTrue(result.isPresent()); 20 | assertEquals(new RequestRunReply(false, null, false, null), result.get()); 21 | } 22 | 23 | @Test 24 | void deserializeWithAllOptionals() throws JsonProcessingException { 25 | String json = 26 | "{\"bench\": true, \"bench_hash\": \"yay, I'm a hash\", \"run\": true, \"run_id\": \"576afdcb-eaf9-46b2-9287-fc3bf8df83df\"}"; 27 | Optional result = serializer.deserialize(json, RequestRunReply.class); 28 | 29 | UUID uuid = UUID.fromString("576afdcb-eaf9-46b2-9287-fc3bf8df83df"); 30 | assertTrue(result.isPresent()); 31 | assertEquals(new RequestRunReply(true, "yay, I'm a hash", true, uuid), result.get()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/restapi/jsonobjects/JsonRunDescription.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.jsonobjects; 2 | 3 | import de.aaaaaaah.velcom.backend.access.benchmarkaccess.entities.Measurement; 4 | import de.aaaaaaah.velcom.backend.access.benchmarkaccess.entities.RunError; 5 | import de.aaaaaaah.velcom.shared.util.Either; 6 | import java.util.Collection; 7 | import java.util.UUID; 8 | 9 | public class JsonRunDescription { 10 | 11 | private final UUID id; 12 | private final long startTime; 13 | private final JsonSuccess success; 14 | private final JsonSource source; 15 | 16 | public JsonRunDescription(UUID id, long startTime, JsonSuccess success, JsonSource source) { 17 | this.id = id; 18 | this.startTime = startTime; 19 | this.success = success; 20 | this.source = source; 21 | } 22 | 23 | public UUID getId() { 24 | return id; 25 | } 26 | 27 | public long getStartTime() { 28 | return startTime; 29 | } 30 | 31 | public JsonSuccess getSuccess() { 32 | return success; 33 | } 34 | 35 | public JsonSource getSource() { 36 | return source; 37 | } 38 | 39 | public enum JsonSuccess { 40 | SUCCESS, 41 | PARTIAL_SUCCESS, 42 | FAILURE; 43 | 44 | public static JsonSuccess fromRunResult(Either> result) { 45 | return result 46 | .getRight() 47 | .map( 48 | ms -> ms.stream().allMatch(m -> m.getContent().isRight()) ? SUCCESS : PARTIAL_SUCCESS) 49 | .orElse(FAILURE); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VelCom 2 | 3 | VelCom lets you progressively benchmark one or more projects and monitor or 4 | compare the benchmark results. This makes it easier to notice performance 5 | regressions or see how different branches compare. It also lets you visualize 6 | how different metrics have developed over time. 7 | 8 | Here are a few links to get started: 9 | - [Installation](docs/install.md) 10 | - [CLI readme](cli/README.md) 11 | - [Benchmark repo specification](docs/bench_repo_specification.md) 12 | - [Guide to contributing](docs/contributing.md) 13 | 14 | ## Uploading a tar via curl 15 | 16 | If you quickly want to benchmark your current directory from the command line, 17 | run this command, replacing `VELCOM_ADDRESS` and `ADMIN_PASSWORD` with the 18 | proper values: 19 | 20 | ``` 21 | tar -czO . | curl VELCOM_ADDRESS/api/queue/upload/tar \ 22 | -F 'file=@-;filename=file.tar.gz' \ 23 | -F description='console upload' \ 24 | -u admin:ADMIN_PASSWORD 25 | ``` 26 | 27 | If you want to assign the tar to a specific repository, append this flag to the 28 | command above, replacing `REPO_ID` with the repo's UUID (can be found on the 29 | repo detail page): 30 | 31 | ``` 32 | -F repo_id=REPO_ID 33 | ``` 34 | 35 | Here is an example with all of the above: 36 | 37 | ``` 38 | tar -czO . | curl https://velcom.aaaaaaah.de/api/queue/upload/tar \ 39 | -F 'file=@-;filename=file.tar.gz' \ 40 | -F description='console upload' \ 41 | -u admin:12345 \ 42 | -F repo_id=44bb5c8d-b20d-4bef-bdad-c92767dfa489 43 | ``` 44 | 45 | For a nicer looking result, pipe the output from `curl` into `jq`. 46 | -------------------------------------------------------------------------------- /backend/shared/src/test/java/de/aaaaaaah/velcom/shared/protocol/serialization/clientbound/ClientBoundPacketTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.serialization.clientbound; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertTrue; 5 | 6 | import com.fasterxml.jackson.core.JsonProcessingException; 7 | import com.fasterxml.jackson.databind.JsonNode; 8 | import de.aaaaaaah.velcom.shared.protocol.serialization.SerializerBasedTest; 9 | import java.util.Optional; 10 | import org.junit.jupiter.api.Test; 11 | 12 | class ClientBoundPacketTest extends SerializerBasedTest { 13 | 14 | @Test 15 | void serializeCorrectly() throws JsonProcessingException { 16 | ClientBoundPacket packet = 17 | new ClientBoundPacket(ClientBoundPacketType.CLEAR_RESULT, objectMapper.createArrayNode()); 18 | JsonNode packetTree = serializer.serializeTree(packet); 19 | 20 | String expected = "{\"type\": \"clear_result\", \"data\": []}"; 21 | JsonNode expectedTree = objectMapper.readTree(expected); 22 | 23 | assertEquals(expectedTree, packetTree); 24 | } 25 | 26 | @Test 27 | void deserializeCorrectly() { 28 | String json = "{\"type\": \"request_run_reply\", \"data\": {}}"; 29 | Optional result = serializer.deserialize(json, ClientBoundPacket.class); 30 | 31 | assertTrue(result.isPresent()); 32 | assertEquals( 33 | new ClientBoundPacket( 34 | ClientBoundPacketType.REQUEST_RUN_REPLY, objectMapper.createObjectNode()), 35 | result.get()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/access/benchmarkaccess/entities/RunErrorType.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.access.benchmarkaccess.entities; 2 | 3 | /** Describes which type of error has occurred when trying to perform a benchmark run. */ 4 | public enum RunErrorType { 5 | 6 | /** The benchmark script returned a global error. */ 7 | BENCH_SCRIPT_ERROR("BENCH"), 8 | 9 | /** Some error occurred in velcom which prevented the execution of the benchmark script. */ 10 | VELCOM_ERROR("VELCOM"); 11 | 12 | private final String textualRepresentation; 13 | 14 | RunErrorType(String textualRepresentation) { 15 | this.textualRepresentation = textualRepresentation; 16 | } 17 | 18 | public String getTextualRepresentation() { 19 | return textualRepresentation; 20 | } 21 | 22 | /** 23 | * Tries to find the error type that matches the given string representation. 24 | * 25 | * @param representation the representation 26 | * @return the error type that matches the given string representation 27 | * @throws IllegalArgumentException if no error type matches the given string representation 28 | */ 29 | public static RunErrorType fromTextualRepresentation(String representation) 30 | throws IllegalArgumentException { 31 | 32 | for (RunErrorType runErrorType : RunErrorType.values()) { 33 | if (runErrorType.getTextualRepresentation().equals(representation)) { 34 | return runErrorType; 35 | } 36 | } 37 | 38 | throw new IllegalArgumentException("\"" + representation + "\" is not a valid error type"); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/access/taskaccess/entities/TaskPriority.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.access.taskaccess.entities; 2 | 3 | /** The priority a task can have in the queue. Lower means more important. */ 4 | public enum TaskPriority { 5 | /** 6 | * The task has been manually prioritized. In the case of commits, manually adding them to the 7 | * queue automatically prioritizes them. 8 | */ 9 | MANUAL(0), 10 | /** 11 | * The task has been created by a user (e. g. from an uploaded tar file or a GitHub command), but 12 | * should not have priority over manually prioritized tasks. 13 | */ 14 | USER_CREATED(1), 15 | /** 16 | * The task represents a commit that has been automatically added to the queue by the listener. 17 | */ 18 | LISTENER(2); 19 | 20 | private final int priority; 21 | 22 | TaskPriority(int priority) { 23 | this.priority = priority; 24 | } 25 | 26 | /** 27 | * Convert an int to a {@link TaskPriority}. Integers that don't correspond to a particular 28 | * priority are interpreted as the closest available priority. 29 | * 30 | * @param priority the int to convert 31 | * @return the matching priority 32 | */ 33 | public static TaskPriority fromInt(int priority) { 34 | if (priority <= 0) { 35 | return MANUAL; 36 | } else if (priority == 1) { 37 | return USER_CREATED; 38 | } else { 39 | return LISTENER; 40 | } 41 | } 42 | 43 | /** 44 | * @return the priority as an integer 45 | */ 46 | public int asInt() { 47 | return priority; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/restapi/exception/NoSuchDimensionExceptionMapper.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.exception; 2 | 3 | import de.aaaaaaah.velcom.backend.access.dimensionaccess.exceptions.NoSuchDimensionException; 4 | import javax.ws.rs.core.Response; 5 | import javax.ws.rs.core.Response.Status; 6 | import javax.ws.rs.ext.ExceptionMapper; 7 | 8 | /** An {@link ExceptionMapper} that transforms {@link NoSuchDimensionException}s to NOT_FOUND. */ 9 | public class NoSuchDimensionExceptionMapper implements ExceptionMapper { 10 | 11 | @Override 12 | public Response toResponse(NoSuchDimensionException exception) { 13 | return Response.status(Status.NOT_FOUND) 14 | .entity( 15 | new Info( 16 | "could not find dimension", 17 | exception.getInvalidDimension().getBenchmark(), 18 | exception.getInvalidDimension().getMetric())) 19 | .build(); 20 | } 21 | 22 | private static class Info { 23 | 24 | private final String message; 25 | private final String benchmark; 26 | private final String metric; 27 | 28 | public Info(String message, String benchmark, String metric) { 29 | this.message = message; 30 | this.benchmark = benchmark; 31 | this.metric = metric; 32 | } 33 | 34 | public String getMessage() { 35 | return message; 36 | } 37 | 38 | public String getBenchmark() { 39 | return benchmark; 40 | } 41 | 42 | public String getMetric() { 43 | return metric; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/access/benchmarkaccess/entities/sources/TarSource.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.access.benchmarkaccess.entities.sources; 2 | 3 | import de.aaaaaaah.velcom.backend.access.repoaccess.entities.RepoId; 4 | import java.util.Objects; 5 | import java.util.Optional; 6 | import javax.annotation.Nullable; 7 | 8 | /** A source describing that the task originated from a tar. The task may be attached to a repo. */ 9 | public class TarSource { 10 | 11 | private final String description; 12 | @Nullable private final RepoId repoId; 13 | 14 | public TarSource(String description, @Nullable RepoId repoId) { 15 | this.description = Objects.requireNonNull(description); 16 | this.repoId = repoId; 17 | } 18 | 19 | public String getDescription() { 20 | return description; 21 | } 22 | 23 | public Optional getRepoId() { 24 | return Optional.ofNullable(repoId); 25 | } 26 | 27 | @Override 28 | public boolean equals(Object o) { 29 | if (this == o) { 30 | return true; 31 | } 32 | if (o == null || getClass() != o.getClass()) { 33 | return false; 34 | } 35 | TarSource tarSource = (TarSource) o; 36 | return Objects.equals(description, tarSource.description) 37 | && Objects.equals(repoId, tarSource.repoId); 38 | } 39 | 40 | @Override 41 | public int hashCode() { 42 | return Objects.hash(description, repoId); 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | return "TarSource{" + "description='" + description + '\'' + ", repoId=" + repoId + '}'; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /backend/backend/src/main/resources/db/migration/V8__global_dimensions_part_2.sql: -------------------------------------------------------------------------------- 1 | ----------------------------------- 2 | -- Renaming to-be-deleted tables -- 3 | ----------------------------------- 4 | 5 | ALTER TABLE measurement RENAME TO measurement_old; 6 | 7 | ------------------------- 8 | -- Creating new tables -- 9 | ------------------------- 10 | 11 | CREATE TABLE IF NOT EXISTS measurement ( 12 | id CHAR(36) NOT NULL PRIMARY KEY, 13 | run_id CHAR(36) NOT NULL, 14 | benchmark TEXT NOT NULL, 15 | metric TEXT NOT NULL, 16 | unit TEXT, 17 | interpretation TEXT, 18 | error TEXT, 19 | 20 | FOREIGN KEY (run_id) REFERENCES run(id) ON UPDATE CASCADE ON DELETE CASCADE, 21 | FOREIGN KEY (benchmark, metric) REFERENCES dimension(benchmark, metric) ON UPDATE CASCADE ON DELETE CASCADE, 22 | 23 | CHECK (interpretation IN ('LESS_IS_BETTER', 'MORE_IS_BETTER', 'NEUTRAL')) 24 | ); 25 | 26 | ------------------------ 27 | -- Filling new tables -- 28 | ------------------------ 29 | 30 | INSERT INTO measurement 31 | SELECT * 32 | FROM measurement_old; 33 | 34 | ------------------------- 35 | -- Deleting old tables -- 36 | ------------------------- 37 | 38 | DROP TABLE measurement_old; 39 | 40 | -- A little dance to capture all foreign key constraints previously pointing to measurement 41 | ALTER TABLE measurement RENAME TO measurement_old; 42 | ALTER TABLE measurement_old RENAME TO measurement; 43 | 44 | ----------------------------------- 45 | -- Ensure foreign keys are valid -- 46 | ----------------------------------- 47 | 48 | PRAGMA foreign_key_check; 49 | -------------------------------------------------------------------------------- /backend/shared/src/test/java/de/aaaaaaah/velcom/shared/protocol/serialization/serverbound/ServerBoundPacketTest.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.serialization.serverbound; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertTrue; 5 | 6 | import com.fasterxml.jackson.core.JsonProcessingException; 7 | import com.fasterxml.jackson.databind.JsonNode; 8 | import de.aaaaaaah.velcom.shared.protocol.serialization.SerializerBasedTest; 9 | import java.util.Optional; 10 | import org.junit.jupiter.api.Test; 11 | 12 | class ServerBoundPacketTest extends SerializerBasedTest { 13 | 14 | @Test 15 | void serializeCorrectly() throws JsonProcessingException { 16 | ServerBoundPacket packet = 17 | new ServerBoundPacket(ServerBoundPacketType.REQUEST_RUN, objectMapper.createArrayNode()); 18 | JsonNode packetTree = serializer.serializeTree(packet); 19 | 20 | String expected = "{\"type\": \"request_run\", \"data\": []}"; 21 | JsonNode expectedTree = objectMapper.readTree(expected); 22 | 23 | assertEquals(expectedTree, packetTree); 24 | } 25 | 26 | @Test 27 | void deserializeCorrectly() throws JsonProcessingException { 28 | String json = "{\"type\": \"get_status_reply\", \"data\": {}}"; 29 | Optional result = serializer.deserialize(json, ServerBoundPacket.class); 30 | 31 | assertTrue(result.isPresent()); 32 | assertEquals( 33 | new ServerBoundPacket( 34 | ServerBoundPacketType.GET_STATUS_REPLY, objectMapper.createObjectNode()), 35 | result.get()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /frontend/src/plugins/vuetify.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Vuetify from "vuetify/lib/framework"; 3 | 4 | Vue.use(Vuetify); 5 | 6 | export default new Vuetify({ 7 | theme: { 8 | options: { 9 | customProperties: true, 10 | }, 11 | themes: { 12 | light: { 13 | primary: "#3f51b5", 14 | secondary: "#c51162", 15 | accent: "#1faa00", 16 | error: "#e53935", 17 | toolbarColor: "#193a9a", 18 | info: "#3f51b5", 19 | success: "#1faa00", 20 | snackbarSuccess: "#1faa00", 21 | warning: "#c51162", 22 | graphBackground: "#FFFFFF", // white 23 | graphFailedOrUnbenchmarked: "#696969", // dimgrey 24 | graphReferenceElements: "808080", // grey 25 | rowHighlight: "#d3d3d3", 26 | // only used in graphs. Should match normal text color 27 | graphTextColor: "#212121", 28 | }, 29 | dark: { 30 | primary: "#7f9bff", 31 | secondary: "#c51162", 32 | accent: "#eb7b18", 33 | toolbarColor: "#4268c6", 34 | info: "#3f51b5", 35 | success: "#73bf69", 36 | snackbarSuccess: "#3db223", 37 | warning: "#ff9830", 38 | error: "#f2495c", 39 | graphBackground: "#2f3136", 40 | graphFailedOrUnbenchmarked: "#d3d3d3", // lightgray 41 | graphReferenceElements: "#d3d3d3", // lightgray 42 | rowHighlight: "#616161", 43 | // only used in graphs. Should match normal text color 44 | graphTextColor: "#e6ebf8", 45 | }, 46 | }, 47 | }, 48 | icons: { 49 | iconfont: "mdiSvg", 50 | }, 51 | }); 52 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/restapi/jsonobjects/JsonRun.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.jsonobjects; 2 | 3 | import java.util.UUID; 4 | 5 | public class JsonRun { 6 | 7 | private final UUID id; 8 | private final String author; 9 | private final String runnerName; 10 | private final String runnerInfo; 11 | private final long startTime; 12 | private final long stopTime; 13 | private final JsonSource source; 14 | private final JsonResult result; 15 | 16 | public JsonRun( 17 | UUID id, 18 | String author, 19 | String runnerName, 20 | String runnerInfo, 21 | long startTime, 22 | long stopTime, 23 | JsonSource source, 24 | JsonResult result) { 25 | 26 | this.id = id; 27 | this.author = author; 28 | this.runnerName = runnerName; 29 | this.runnerInfo = runnerInfo; 30 | this.startTime = startTime; 31 | this.stopTime = stopTime; 32 | this.source = source; 33 | this.result = result; 34 | } 35 | 36 | public UUID getId() { 37 | return id; 38 | } 39 | 40 | public String getAuthor() { 41 | return author; 42 | } 43 | 44 | public String getRunnerName() { 45 | return runnerName; 46 | } 47 | 48 | public String getRunnerInfo() { 49 | return runnerInfo; 50 | } 51 | 52 | public long getStartTime() { 53 | return startTime; 54 | } 55 | 56 | public long getStopTime() { 57 | return stopTime; 58 | } 59 | 60 | public JsonSource getSource() { 61 | return source; 62 | } 63 | 64 | public JsonResult getResult() { 65 | return result; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /frontend/src/components/queue/WorkerOverview.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 43 | 44 | 50 | -------------------------------------------------------------------------------- /backend/backend/src/main/java/de/aaaaaaah/velcom/backend/restapi/jsonobjects/JsonShortRunDescription.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.backend.restapi.jsonobjects; 2 | 3 | import de.aaaaaaah.velcom.backend.access.benchmarkaccess.entities.ShortRunDescription; 4 | import java.util.UUID; 5 | import javax.annotation.Nullable; 6 | 7 | public class JsonShortRunDescription { 8 | 9 | private final UUID id; 10 | @Nullable private final String commitHash; 11 | @Nullable private final String commitSummary; 12 | @Nullable private final String tarDescription; 13 | 14 | public JsonShortRunDescription( 15 | UUID id, 16 | @Nullable String commitHash, 17 | @Nullable String commitSummary, 18 | @Nullable String tarDescription) { 19 | 20 | this.id = id; 21 | this.commitHash = commitHash; 22 | this.commitSummary = commitSummary; 23 | this.tarDescription = tarDescription; 24 | } 25 | 26 | public static JsonShortRunDescription fromShortRunDescription(ShortRunDescription description) { 27 | return new JsonShortRunDescription( 28 | description.getId().getId(), 29 | description.getCommitHash().orElse(null), 30 | description.getCommitSummary().orElse(null), 31 | description.getTarDescription().orElse(null)); 32 | } 33 | 34 | public UUID getId() { 35 | return id; 36 | } 37 | 38 | @Nullable 39 | public String getCommitHash() { 40 | return commitHash; 41 | } 42 | 43 | @Nullable 44 | public String getCommitSummary() { 45 | return commitSummary; 46 | } 47 | 48 | @Nullable 49 | public String getTarDescription() { 50 | return tarDescription; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /frontend/src/components/misc/ThemeSelector.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 43 | 44 | 60 | -------------------------------------------------------------------------------- /backend/shared/src/main/java/de/aaaaaaah/velcom/shared/protocol/serialization/clientbound/ClientBoundPacketType.java: -------------------------------------------------------------------------------- 1 | package de.aaaaaaah.velcom.shared.protocol.serialization.clientbound; 2 | 3 | import com.fasterxml.jackson.annotation.JsonValue; 4 | import java.util.Optional; 5 | 6 | /** The types of packets that can be sent to the runner by the backend. */ 7 | public enum ClientBoundPacketType { 8 | ABORT_RUN("abort_run", AbortRun.class), 9 | CLEAR_RESULT("clear_result", ClearResult.class), 10 | GET_RESULT("get_result", GetResult.class), 11 | GET_STATUS("get_status", GetStatus.class), 12 | REQUEST_RUN_REPLY("request_run_reply", RequestRunReply.class); 13 | 14 | private final String type; 15 | private final Class dataClass; 16 | 17 | ClientBoundPacketType(String type, Class dataClass) { 18 | this.type = type; 19 | this.dataClass = dataClass; 20 | } 21 | 22 | static Optional ofType(String type) { 23 | for (ClientBoundPacketType value : ClientBoundPacketType.values()) { 24 | if (value.type.equals(type)) { 25 | return Optional.of(value); 26 | } 27 | } 28 | return Optional.empty(); 29 | } 30 | 31 | static Optional ofClass(Class theClass) { 32 | for (ClientBoundPacketType value : ClientBoundPacketType.values()) { 33 | if (value.dataClass.equals(theClass)) { 34 | return Optional.of(value); 35 | } 36 | } 37 | return Optional.empty(); 38 | } 39 | 40 | @JsonValue 41 | public String getType() { 42 | return type; 43 | } 44 | 45 | public Class getDataClass() { 46 | return dataClass; 47 | } 48 | } 49 | --------------------------------------------------------------------------------