├── docs ├── evaluation │ ├── adr-parsing │ │ ├── cypress.json │ │ ├── cypress │ │ │ ├── fixtures │ │ │ │ ├── CounterAdrsAllRepos.json │ │ │ │ ├── CounterDiffAllRepos.json │ │ │ │ ├── CounterDiffPerRepo.json │ │ │ │ └── CounterAdrsPerRepo.json │ │ │ └── integration │ │ │ │ └── EmpiricalAnalysis.js │ │ ├── .gitignore │ │ ├── README.md │ │ └── adr-parsing-manual-results.csv │ └── user-study │ │ ├── results │ │ ├── evaluation-data-synthesized.xlsx │ │ ├── participant-statements.drawio │ │ ├── participants.csv │ │ ├── analysis-script.R │ │ └── case-descriptions │ │ │ ├── case-description-06.md │ │ │ ├── case-description-09.md │ │ │ ├── case-description-07.md │ │ │ ├── case-description-05.md │ │ │ ├── case-description-08.md │ │ │ ├── case-description-01.md │ │ │ ├── case-description-02.md │ │ │ ├── case-description-03.md │ │ │ └── case-description-04.md │ │ ├── material-during-study │ │ ├── task-list.md │ │ ├── interview-guide.md │ │ └── meeting-summary.md │ │ ├── material-before-study │ │ ├── study-procedure.md │ │ └── callout-for-participation.md │ │ └── README.md ├── ui-sketches │ ├── README.md │ ├── create-adr.png │ ├── home-page.png │ ├── repo-list.png │ ├── complete-editor.png │ └── repo-selection.png ├── img │ └── editor-screenshot.png ├── architecture │ ├── sequence-diagram-GitHub.pdf │ └── FMC-Diagram.drawio └── adr │ ├── 0001-use-vue.js.md │ ├── 0003-use-pizzly-as-backend.md │ ├── 0000-use-markdown-architectural-decision-records.md │ ├── 0002-use-antlr-for-parsing-adrs.md │ └── 0004-implement-a-global-store.md ├── public └── favicon.ico ├── src ├── assets │ └── logo.png ├── App.vue ├── views │ ├── ErrorPage.vue │ └── LandingPage.vue ├── plugins │ ├── apiConfig │ │ └── config.js │ ├── firebase │ │ └── client.js │ ├── parser │ │ ├── README.md │ │ ├── MADR.g4 │ │ └── MADRListener.js │ └── router.js ├── components │ ├── EditorMarkdownPreview.vue │ ├── HelpIcon.vue │ ├── ToolbarMenuMode.vue │ ├── DatePickerMenu.vue │ ├── ConnectToGitHubButton.vue │ ├── EditorRaw.vue │ ├── DialogDeleteAdr.vue │ ├── DialogRemoveRepository.vue │ ├── EditorMadrDecisionOutcome.vue │ ├── EditorMadrCodemirror.vue │ ├── EditorConvert.vue │ ├── EditorMadrStatusDateDecidersStory.vue │ ├── Editor.vue │ ├── EditorMadr.vue │ └── EditorMadrList.vue └── main.js ├── babel.config.json ├── .prettierrc.json ├── .prettierignore ├── cypress.config.js ├── .editorconfig ├── vite.config.js ├── tests ├── classes.test.js ├── api-searchRepositoryList.test.js └── parser.test.js ├── cypress ├── support │ ├── commands.js │ └── e2e.js ├── plugins │ └── index.js └── e2e │ └── adrManagerTest │ ├── DeleteRepo.cy.js │ ├── AddRepo.cy.js │ ├── DeleteAdr.cy.js │ ├── Routing.cy.js │ ├── Parser.cy.js │ ├── AddNewAdr.cy.js │ ├── Modes.cy.js │ └── PushNewAdr.cy.js ├── .github └── workflows │ ├── publish.yml │ └── tests.yml ├── index.html ├── LICENSE ├── package.json ├── jest.config.js ├── .gitignore └── README.md /docs/evaluation/adr-parsing/cypress.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adr/adr-manager/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adr/adr-manager/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /docs/evaluation/adr-parsing/cypress/fixtures/CounterAdrsAllRepos.json: -------------------------------------------------------------------------------- 1 | { 2 | "counter": 356 3 | } -------------------------------------------------------------------------------- /docs/evaluation/adr-parsing/cypress/fixtures/CounterDiffAllRepos.json: -------------------------------------------------------------------------------- 1 | { 2 | "counter": 234 3 | } -------------------------------------------------------------------------------- /docs/ui-sketches/README.md: -------------------------------------------------------------------------------- 1 | # Interactive UI Design Board 2 | 3 | https://balsamiq.cloud/skfc9uc/pzbcu31 4 | -------------------------------------------------------------------------------- /babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["@babel/preset-env", { "targets": { "node": "current" } }]] 3 | } 4 | -------------------------------------------------------------------------------- /docs/img/editor-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adr/adr-manager/HEAD/docs/img/editor-screenshot.png -------------------------------------------------------------------------------- /docs/ui-sketches/create-adr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adr/adr-manager/HEAD/docs/ui-sketches/create-adr.png -------------------------------------------------------------------------------- /docs/ui-sketches/home-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adr/adr-manager/HEAD/docs/ui-sketches/home-page.png -------------------------------------------------------------------------------- /docs/ui-sketches/repo-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adr/adr-manager/HEAD/docs/ui-sketches/repo-list.png -------------------------------------------------------------------------------- /docs/ui-sketches/complete-editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adr/adr-manager/HEAD/docs/ui-sketches/complete-editor.png -------------------------------------------------------------------------------- /docs/ui-sketches/repo-selection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adr/adr-manager/HEAD/docs/ui-sketches/repo-selection.png -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "none", 3 | "semi": true, 4 | "singleQuote": false, 5 | "printWidth": 120 6 | } 7 | -------------------------------------------------------------------------------- /docs/architecture/sequence-diagram-GitHub.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adr/adr-manager/HEAD/docs/architecture/sequence-diagram-GitHub.pdf -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore artifacts: 2 | dist 3 | coverage 4 | node_modules 5 | src/assets 6 | src/plugins/parser 7 | package-lock.json 8 | public 9 | docs 10 | -------------------------------------------------------------------------------- /docs/evaluation/adr-parsing/.gitignore: -------------------------------------------------------------------------------- 1 | cypress/plugins/index.js 2 | cypress/screenshots/* 3 | cypress/support/commands.js 4 | cypress/support/index.js 5 | cypress/videos/* 6 | -------------------------------------------------------------------------------- /docs/evaluation/user-study/results/evaluation-data-synthesized.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adr/adr-manager/HEAD/docs/evaluation/user-study/results/evaluation-data-synthesized.xlsx -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | -------------------------------------------------------------------------------- /src/views/ErrorPage.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 15 | -------------------------------------------------------------------------------- /cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | import vitePreprocessor from "cypress-vite"; 3 | 4 | export default defineConfig({ 5 | projectId: "shyc93", 6 | defaultCommandTimeout: 8000, 7 | e2e: { 8 | setupNodeEvents(on) { 9 | on("file:preprocessor", vitePreprocessor()); 10 | } 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /src/plugins/apiConfig/config.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | const BASE_URL_USER = "https://api.github.com/user"; 3 | const BASE_URL_REPO = "https://api.github.com/repos"; 4 | const setHeaders = () => { 5 | axios.defaults.headers.common["Authorization"] = `Bearer ${localStorage?.getItem("authId")}`; 6 | }; 7 | export { BASE_URL_REPO, BASE_URL_USER, setHeaders }; 8 | -------------------------------------------------------------------------------- /docs/adr/0001-use-vue.js.md: -------------------------------------------------------------------------------- 1 | # Use Vue.js 2 | 3 | * Status: accepted 4 | 5 | ## Context and Problem Statement 6 | 7 | A framework makes the creation of a web app significantly easier. Which framework should be used? 8 | 9 | ## Considered Options 10 | 11 | * Vue.js 12 | * Angular 13 | 14 | ## Decision Outcome 15 | 16 | Chosen option: "Vue.js", because the team has more experience with Vue.js. 17 | -------------------------------------------------------------------------------- /docs/evaluation/user-study/material-during-study/task-list.md: -------------------------------------------------------------------------------- 1 | # Task List 2 | 3 | 1. Read the meeting summary. 4 | 2. Open in your browser. 5 | 3. Connect to the ADR Manager using the test account for authentication. 6 | 4. Add the test repository (the account only has access to one repository). 7 | 5. Create a new ADR. 8 | 6. Fill in the information from the meeting protocol. 9 | 7. Push the ADR to GitHub. 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | 11 | # Set default charset 12 | charset = utf-8 13 | 14 | # 4 space indentation 15 | indent_style = space 16 | indent_size = 4 17 | 18 | # only two spaces for YAML and Markdown 19 | [*.{yml,md}] 20 | indent_size = 2 21 | -------------------------------------------------------------------------------- /src/components/EditorMarkdownPreview.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 17 | 18 | 23 | -------------------------------------------------------------------------------- /src/components/HelpIcon.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 25 | -------------------------------------------------------------------------------- /src/plugins/firebase/client.js: -------------------------------------------------------------------------------- 1 | // Import the functions you need from the SDKs you need 2 | import { initializeApp } from "firebase/app"; 3 | import { getAuth, GithubAuthProvider } from "firebase/auth"; 4 | 5 | const firebaseConfig = { 6 | apiKey: "AIzaSyDLeDiHmseMx-w9Ba7TI4lL8Lvc0M7Zpeg", 7 | authDomain: "adr-manager.firebaseapp.com", 8 | projectId: "adr-manager", 9 | storageBucket: "adr-manager.appspot.com", 10 | messagingSenderId: "313525337788", 11 | appId: "1:313525337788:web:0834a7a168fe39352adab9" 12 | }; 13 | 14 | // Initialize Firebase 15 | const app = initializeApp(firebaseConfig); 16 | const auth = getAuth(app); 17 | const GithubProvider = new GithubAuthProvider(); 18 | 19 | export { app, auth, GithubProvider }; 20 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { createVuePlugin } from "vite-plugin-vue2"; 2 | import { VuetifyResolver } from "unplugin-vue-components/resolvers"; 3 | import Components from "unplugin-vue-components/vite"; 4 | 5 | export default { 6 | server: { 7 | host: "localhost", 8 | port: 8000 9 | }, 10 | // URL base for production 11 | base: "/adr-manager/", 12 | // required for Vuetify (see https://github.com/vuetifyjs/vuetify/discussions/4068#discussioncomment-1357093) 13 | resolve: { 14 | alias: { 15 | vue: "vue/dist/vue.runtime.esm.js" 16 | }, 17 | dedupe: ["vue", "vuetify"] 18 | }, 19 | plugins: [ 20 | createVuePlugin(), 21 | Components({ 22 | resolvers: [VuetifyResolver()] 23 | }) 24 | ] 25 | }; 26 | -------------------------------------------------------------------------------- /src/plugins/parser/README.md: -------------------------------------------------------------------------------- 1 | # Parser with Antlr4 2 | 3 | - The required version of Antlr is 4.9. 4 | - Installation guides of Antlr4: 5 | 6 | - Windows: 7 | - UNIX: 8 | 9 | - Explanation for using Antlr4 with JavaScript: 10 | - The shell command to build the parser is as follows: `antlr4 -Dlanguage=JavaScript MADR.g4`. 11 | This should create the files `MADRLexer.js`, `MADRListener.js`, and `MADRParser.js`. 12 | 13 | ## Windows 14 | 15 | 1. Install Java 16 | 2. Download the `jar` from 17 | 3. `java -jar c:\Users\{username}\Downloads\antlr-4.9.2-complete.jar -Dlanguage=JavaScript MADR.g4` 18 | -------------------------------------------------------------------------------- /tests/classes.test.js: -------------------------------------------------------------------------------- 1 | import { createShortTitle } from "../src/plugins/classes.js"; 2 | 3 | test("Markdown link followed by text", () => { 4 | expect( 5 | createShortTitle("[MADR](https://adr.github.io/madr/) 2.1.2 – The Markdown Architectural Decision Records") 6 | ).toBe("MADR 2.1.2"); 7 | }); 8 | 9 | test("Markdown link preceded by text", () => { 10 | expect(createShortTitle("Include in [adr-tools](https://github.com/npryce/adr-tools)")).toBe( 11 | "Include in adr-tools" 12 | ); 13 | }); 14 | 15 | test("Wrong balancing of brackets", () => { 16 | expect(createShortTitle("Include in [adr-tools](https://github.com/npryce/adr-tools")).toBe( 17 | "Include in [adr-tools](https://github.com/npryce/adr-tools" 18 | ); 19 | }); 20 | 21 | test("Single closing brace", () => { 22 | expect(createShortTitle("Con. Opt 1)")).toBe("Con. Opt 1)"); 23 | }); 24 | -------------------------------------------------------------------------------- /cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add("login", (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This will overwrite an existing command -- 25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /docs/adr/0003-use-pizzly-as-backend.md: -------------------------------------------------------------------------------- 1 | # Use Pizzly as Backend 2 | 3 | * Status: accepted 4 | 5 | ## Context and Problem Statement 6 | 7 | Should the ADR Manager have a backend? If so, how should it look? 8 | 9 | ## Considered Options 10 | 11 | * Use a Python back end 12 | * Don't use a back end 13 | * Use Pizzly as Backend 14 | 15 | ## Decision Outcome 16 | 17 | Chosen option: "Use Pizzly as Backend", because comes out best (see below). 18 | 19 | ## Pros and Cons of the Options 20 | 21 | ### Use a Python back end 22 | 23 | * Good, because GitHub interaction can be achieved. 24 | * Bad, because hard to maintain. 25 | 26 | ### Don't use a back end 27 | 28 | * Bad, because we can't authorize to GitHub without back end. 29 | 30 | ### Use Pizzly as Backend 31 | 32 | [Pizzly](https://github.com/Bearer/Pizzly/) can handle the OAuth dance for us. It can be hosted on Heroku as the back end. 33 | 34 | * Good, because less work than writing the back end from scratch. 35 | * Good, because we don't have to handle the authorization process. 36 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: build and publish 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - publish 8 | paths-ignore: 9 | - "**/docs/**" 10 | - "**/README.md" 11 | - "**/LICENSE" 12 | - "**/.editorconfig" 13 | - "**/.prettierrc.json" 14 | - "**/.gitignore" 15 | workflow_dispatch: 16 | 17 | concurrency: 18 | group: publish 19 | cancel-in-progress: true 20 | 21 | jobs: 22 | publish: 23 | name: Publish 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v2 27 | - uses: actions/setup-node@v2 28 | with: 29 | node-version: "18" 30 | - run: npm ci 31 | - run: npm run build 32 | - name: Deploy 33 | uses: peaceiris/actions-gh-pages@v3 34 | with: 35 | github_token: ${{ secrets.GITHUB_TOKEN }} 36 | publish_dir: ./dist 37 | force_orphan: true 38 | user_name: "github-actions[bot]" 39 | user_email: "github-actions[bot]@users.noreply.github.com" 40 | -------------------------------------------------------------------------------- /cypress/plugins/index.js: -------------------------------------------------------------------------------- 1 | /// 2 | // *********************************************************** 3 | // This example plugins/index.js can be used to load plugins 4 | // 5 | // You can change the location of this file or turn off loading 6 | // the plugins file with the 'pluginsFile' configuration option. 7 | // 8 | // You can read more here: 9 | // https://on.cypress.io/plugins-guide 10 | // *********************************************************** 11 | 12 | // This function is called when a project is opened or re-opened (e.g. due to 13 | // the project's config changing) 14 | 15 | /** 16 | * @type {Cypress.PluginConfig} 17 | */ 18 | export default (on, config) => { 19 | // `on` is used to hook into various events Cypress emits 20 | // `config` is the resolved Cypress config 21 | require("@cypress/code-coverage/task")(on, config); 22 | // include any other plugin code... 23 | 24 | // It's IMPORTANT to return the config object 25 | // with any changed environment variables 26 | return config; 27 | }; 28 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 13 | 14 | 15 | ADR Manager 16 | 17 | 18 | 19 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /cypress/support/e2e.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import "./commands"; 18 | // cypress/support/index.js 19 | import "@cypress/code-coverage/support"; 20 | 21 | export const TEST_BASE_URL = "http://localhost:8000/adr-manager/#/manager"; 22 | export const REST_LIST_REPO_URL = "**/user/repos**"; 23 | export const REST_REPO_URL = "**/repos/**"; 24 | export const REST_BRANCH_URL = "**/repos/**/branches/**"; 25 | export const REST_COMMIT_URL = "**/repos/**/git/commits?**"; 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Oliver Kopp 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 | -------------------------------------------------------------------------------- /docs/evaluation/user-study/material-before-study/study-procedure.md: -------------------------------------------------------------------------------- 1 | # Study Procedure 2 | 3 | During the first part of the study, the participants will use the tool to solve a small written task while thinking aloud. 4 | The second part is a short semi-structured interview. 5 | 6 | ## Prerequisites 7 | 8 | Provided by participants: 9 | 10 | * PC or laptop with mouse, keyboard, microphone and Internet access 11 | * Webex 12 | * Webcam (if possible but not necessary) 13 | * Internet browser (preferably Chrome or Firefox) 14 | 15 | Provided by the organizers during the study: 16 | 17 | * Small explanation and task list 18 | * Access to GitHub through a test account 19 | * A meeting summary with an architecture decision 20 | 21 | ## Study Procedure 22 | 23 | * The screen is shared using Webex. 24 | * The organizers will record the shared screen and the audio. 25 | * The participant is given the login data for a test GitHub account. 26 | * The participant is given a meeting summary. 27 | * Next, the participant will work on the task list. 28 | * The participant talks about their thought process while performing the tasks. 29 | * After the think-aloud protocol, there will be an interview. 30 | -------------------------------------------------------------------------------- /cypress/e2e/adrManagerTest/DeleteRepo.cy.js: -------------------------------------------------------------------------------- 1 | import { TEST_BASE_URL, REST_LIST_REPO_URL } from "../../support/e2e"; 2 | 3 | context("Deleting repositories", () => { 4 | it("Remove a repo", () => { 5 | window.localStorage.clear(); 6 | window.localStorage.setItem("authId", Cypress.env("OAUTH_E2E_AUTH_ID")); 7 | window.localStorage.setItem("user", Cypress.env("USER")); 8 | cy.visit(TEST_BASE_URL); 9 | // add ADR Manager repo 10 | cy.intercept("GET", REST_LIST_REPO_URL).as("getRepos"); 11 | cy.get("[data-cy=addRepo]").click(); 12 | cy.wait("@getRepos").its("response.statusCode").should("eq", 200); 13 | cy.get("[data-cy=listRepo]").contains("ADR-Manager").click(); 14 | cy.get("[data-cy=addRepoDialog]").click(); 15 | cy.get("[data-cy=repoNameList]").click(); 16 | 17 | // delete repo 18 | cy.get("[data-cy=removeRepo]").click(); 19 | cy.get("[data-cy=removeRepoBtn]").click(); 20 | cy.get("[data-cy=listRepo]").should("have.length", 0); 21 | cy.get("[data-cy=addRepo]").should(() => { 22 | expect(localStorage.getItem("addedRepositories")).to.eq("[]"); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /tests/api-searchRepositoryList.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Test of the function md2adr. 3 | */ 4 | 5 | global.localStorage = { 6 | getItem() { 7 | return "abc...."; 8 | } 9 | }; 10 | import { searchRepositoryList } from "../src/plugins/api.js"; 11 | import { searchTermRepoPairs, mockedValues } from "./constants.js"; 12 | import axios from "axios"; 13 | 14 | jest.mock("axios"); 15 | 16 | for (let i = 0; i < searchTermRepoPairs.length; i++) { 17 | let searchTerm = searchTermRepoPairs[i].searchTerm; 18 | let expectedResult = searchTermRepoPairs[i].results; 19 | axios.get.mockImplementation(() => Promise.resolve(mockedValues)); 20 | 21 | test("Test Searching Repos with list in parameter. Searching for " + searchTerm, async () => { 22 | let list = []; 23 | // Test the function 24 | await searchRepositoryList(searchTerm, 2, list); 25 | expect(list).toStrictEqual(expectedResult); 26 | }); 27 | test("Test Searching Repos without passing list. Searching for " + searchTerm, async () => { 28 | let list = []; 29 | await searchRepositoryList(searchTerm, 3).then((res) => { 30 | list = res; 31 | }); 32 | expect(list).toStrictEqual(expectedResult.slice(0, 30)); 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /docs/evaluation/user-study/README.md: -------------------------------------------------------------------------------- 1 | # Qualitative User Study 2 | 3 | ## Research Objective 4 | 5 | The purpose of this study is to evaluate the functional suitability and usability of the ADR Manager, a tool to support the creation and editing of ADRs. 6 | By involving software professionals who have experience with ADRs, we hope to gain insights from the practical usage of the tool. 7 | 8 | ## Research Question 9 | 10 | - How do software professionals perceive the functional suitability of the ADR Manager? 11 | - How do software professionals perceive the usability of the ADR Manager? 12 | - Which improvements do software professionals suggest for the ADR Manager? 13 | 14 | ## Artifacts 15 | 16 | The following artifacts are available: 17 | 18 | - Materials used before the study, such as a [call for participation](material-before-study/callout-for-participation.md) or the [study procedure](material-before-study/study-procedure.md) 19 | - Materials used during the study, such as the [participant task list](material-during-study/task-list.md) or the [interview guide](material-during-study/interview-guide.md) 20 | - All result artifacts, such as the [case descriptions](results/case-descriptions/), [raw data](results/evaluation-data-raw.csv), or [synthesized data](results/evaluation-data-synthesized.xlsx) 21 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - "**/docs/**" 7 | - "**/README.md" 8 | - "**/LICENSE" 9 | - "**/.editorconfig" 10 | - "**/.eslintignore" 11 | - "**/.gitignore" 12 | workflow_dispatch: 13 | 14 | concurrency: 15 | group: tests 16 | cancel-in-progress: true 17 | 18 | jobs: 19 | unit-tests: 20 | name: Run unit tests with Jest 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@v2 24 | - uses: actions/setup-node@v2 25 | with: 26 | node-version: "18" 27 | - run: npm ci 28 | - run: npm run test 29 | e2e-tests: 30 | name: Run on Cypress e2e tests on Chrome 31 | runs-on: ubuntu-latest 32 | container: cypress/browsers:node-18.16.0-chrome-113.0.5672.92-1-ff-113.0-edge-113.0.1774.35-1 33 | steps: 34 | - uses: actions/checkout@v2 35 | - name: Install dependencies and verify Cypress 36 | run: npm ci 37 | $(npm bin)/cypress verify 38 | - name: Run Cypress tests 39 | env: 40 | CYPRESS_OAUTH_E2E_AUTH_ID: ${{ secrets.OAUTH_E2E_AUTH_ID }} 41 | CYPRESS_USER: ${{ secrets.USER }} 42 | run: | 43 | npm start & 44 | npm run e2e:test-ci 45 | continue-on-error: false 46 | -------------------------------------------------------------------------------- /docs/evaluation/adr-parsing/cypress/fixtures/CounterDiffPerRepo.json: -------------------------------------------------------------------------------- 1 | { 2 | "adr-manager-anonymous/adr-j": 6, 3 | "adr-manager-anonymous/adr-log": 7, 4 | "adr-manager-anonymous/adr-tools": 0, 5 | "adr-manager-anonymous/architectural-decision-records": 0, 6 | "adr-manager-anonymous/blueprint": 19, 7 | "adr-manager-anonymous/cloud-on-k8s": 20, 8 | "adr-manager-anonymous/complaitkubernetes": 0, 9 | "adr-manager-anonymous/core": 4, 10 | "adr-manager-anonymous/digital-paper-edit-client": 10, 11 | "adr-manager-anonymous/digital-paper-edit-firebase": 14, 12 | "adr-manager-anonymous/docker-texlive": 2, 13 | "adr-manager-anonymous/docs": 33, 14 | "adr-manager-anonymous/e-adr": 4, 15 | "adr-manager-anonymous/ecar-charge-pricing": 0, 16 | "adr-manager-anonymous/experimenter": 8, 17 | "adr-manager-anonymous/fxa": 28, 18 | "adr-manager-anonymous/gadr": 2, 19 | "adr-manager-anonymous/island.is": 4, 20 | "adr-manager-anonymous/jabref": 10, 21 | "adr-manager-anonymous/log4brains": 37, 22 | "adr-manager-anonymous/madr": 7, 23 | "adr-manager-anonymous/monocle": 10, 24 | "adr-manager-anonymous/nodejs.dev": 4, 25 | "adr-manager-anonymous/odh-manifests": 3, 26 | "adr-manager-anonymous/python-tuf": 10, 27 | "adr-manager-anonymous/raiden": 12, 28 | "adr-manager-anonymous/raiden-services": 0, 29 | "adr-manager-anonymous/Sylius": 0, 30 | "adr-manager-anonymous/tuleap": 0, 31 | "adr-manager-anonymous/winery": 36 32 | } -------------------------------------------------------------------------------- /docs/evaluation/adr-parsing/cypress/fixtures/CounterAdrsPerRepo.json: -------------------------------------------------------------------------------- 1 | { 2 | "adr-manager-anonymous/adr-j": 6, 3 | "adr-manager-anonymous/adr-log": 7, 4 | "adr-manager-anonymous/adr-tools": 0, 5 | "adr-manager-anonymous/architectural-decision-records": 0, 6 | "adr-manager-anonymous/blueprint": 21, 7 | "adr-manager-anonymous/cloud-on-k8s": 20, 8 | "adr-manager-anonymous/complaitkubernetes": 0, 9 | "adr-manager-anonymous/core": 4, 10 | "adr-manager-anonymous/digital-paper-edit-client": 10, 11 | "adr-manager-anonymous/digital-paper-edit-firebase": 14, 12 | "adr-manager-anonymous/docker-texlive": 5, 13 | "adr-manager-anonymous/docs": 35, 14 | "adr-manager-anonymous/e-adr": 4, 15 | "adr-manager-anonymous/ecar-charge-pricing": 0, 16 | "adr-manager-anonymous/experimenter": 8, 17 | "adr-manager-anonymous/fxa": 29, 18 | "adr-manager-anonymous/gadr": 4, 19 | "adr-manager-anonymous/island.is": 4, 20 | "adr-manager-anonymous/jabref": 25, 21 | "adr-manager-anonymous/log4brains": 37, 22 | "adr-manager-anonymous/madr": 15, 23 | "adr-manager-anonymous/monocle": 13, 24 | "adr-manager-anonymous/nodejs.dev": 4, 25 | "adr-manager-anonymous/odh-manifests": 3, 26 | "adr-manager-anonymous/python-tuf": 11, 27 | "adr-manager-anonymous/raiden": 12, 28 | "adr-manager-anonymous/raiden-services": 4, 29 | "adr-manager-anonymous/Sylius": 15, 30 | "adr-manager-anonymous/tuleap": 10, 31 | "adr-manager-anonymous/winery": 40 32 | } -------------------------------------------------------------------------------- /docs/adr/0000-use-markdown-architectural-decision-records.md: -------------------------------------------------------------------------------- 1 | # Use Markdown Architectural Decision Records 2 | 3 | ## Context and Problem Statement 4 | 5 | We want to record architectural decisions made in this project. 6 | Which format and structure should these records follow? 7 | 8 | ## Considered Options 9 | 10 | * [MADR](https://adr.github.io/madr/) 2.1.2 – The Markdown Architectural Decision Records 11 | * [Michael Nygard's template](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) – The first incarnation of the term "ADR" 12 | * [Sustainable Architectural Decisions](https://www.infoq.com/articles/sustainable-architectural-design-decisions) – The Y-Statements 13 | * Other templates listed at 14 | * Formless – No conventions for file format and structure 15 | 16 | ## Decision Outcome 17 | 18 | Chosen option: "MADR 2.1.2", because 19 | 20 | * Implicit assumptions should be made explicit. 21 | Design documentation is important to enable people understanding the decisions later on. 22 | See also [A rational design process: How and why to fake it](https://doi.org/10.1109/TSE.1986.6312940). 23 | * The MADR format is lean and fits our development style. 24 | * The MADR structure is comprehensible and facilitates usage & maintenance. 25 | * The MADR project is vivid. 26 | * Version 2.1.2 is the latest one available when starting to document ADRs. 27 | -------------------------------------------------------------------------------- /docs/evaluation/user-study/results/participant-statements.drawio: -------------------------------------------------------------------------------- 1 | 7Vldj5pAFP01JtuHNsAA4uPW1d00bdPUNO0+NSNcYLbAmGHwY399BxjkS622KiT2xXjP3IGZueeeIzhA43D9yPDC/0QdCAaa4qwH6GGgafrIEp8psMkBZKEc8BhxckgtgRl5BQkqEk2IA3EtkVMacLKogzaNIrB5DcOM0VU9zaVB/a4L7EELmNk4aKPficN9iarmqBx4AuL58taWNswHQlwky53EPnboqgKhyQCNGaU8/xauxxCkZ1ecSz5vumd0uzAGET9mwlf68uzxKeLJzw/GfPy61GaPb2UxljhI5IblYvmmOAFwxIHIkDLuU49GOJiU6HtGk8iB9DaKiMqcj5QuBKgK8AU438jq4oRTAfk8DOSo2AHb/EjnvzOK8FleLgse1rVoI6N8rekC956JhGKaMBsOHETBLcw84AfytG3lBOOBhiDWI+YxCDAny/o6sOSet82TU+8Zw5tKwoKSiMeVK39JAZEgu8iwjPyKsonMRqUb6frBdPElv38RVTZSQhl5TiCS2iLSfTZpxjGHELLtKXfG6E2LXyV7UiqsfMJhtsBZsVZCUupMcWnECzJu45xWKpLxFIckSLf+BMESOLFxOkCCYEwDyrKbIgeD5doCjzmjv6AyYtoWzN0tuZbAOKwP06tNh6JuhYrJQhRSsKooiSExvyIiSNlPoEoZ/6JK7TJ12++1bi+b//L9rh3Z76qyu8KXbfhmB6tNbW/ko5Hyb/mWdQWJ6JvZ9J98nbjN1cmnX4F8Wot70ySyOUmpQ3h6PHfaDnP6gx3Vvets5mSA5ei7zMnS5sg0z2NOesOcrK7NadgveVA7kwf9SHkwu1AH80R1GFqnqUMr/xrWNPpPvdOoZ90E9a5hTHqLet9iPCeFKSGlJ6bkuq5m73xicsy5aZzJlMy+mZLRC2U4Y4cf+6JDP0eHt1tYqdd31Khbvnw56dArjuZvyCYB8v21LnThNy9NhbrGmxezxdCJ6xKbQGRnCjK8aQFRh6hjBbHaAi9Ol8UcR05F6DV023VCXdepWFC1kdYk5iTyBDoFzBMGmSX3pFDdPCZ231Bq+1n+M6zqNdLM2y7SBbtJhOWfVrmJlf/8oclv -------------------------------------------------------------------------------- /docs/evaluation/user-study/results/participants.csv: -------------------------------------------------------------------------------- 1 | ID,Role,Affiliation,Years of Experience,Years of ADR Experience,Has Used MADR,Other Used ADR Templates and Tools,Preferred Editor Modes,Would Switch Modes,Task Duration in min,Functional Suitability Likert,Usability Likert,Future Usage Likert,Think-Aloud Interference 2 | 1,professor,academia,20,10,no,"Jira, Markdown files",Basic & Professional,maybe,18,4,3,2,"Find ""Commit and Push"" button" 3 | 2,"professor, consultant",both,32,20,yes,"Y-statements, Markdown files, embedded ADRs",Basic & Professional,yes,18,4,5,5,Help with editor modes 4 | 3,"PhD student, developer",both,3,2,no,"text files, pen and paper",custom preferences,maybe,13,5,4,3,Help with editor modes 5 | 4,architect,industry,10,5,no,"custom project-specific templates, no special tools",Basic & Professional,maybe,14,4,4,3,"Help with editor modes and list behavior of ""considered options""" 6 | 5,architect,industry,4,6,yes,mostly GitHub editor,Basic & Professional,yes,18,4,4,5,Help with editor modes 7 | 6,architect,industry,10,5,no,"custom template, Azure DevOps wiki",Professional,no,11,5,4,3,Help with editor modes 8 | 7,architect,industry,20,10,no,"custom project-specific templates, Confluence",Professional,yes,10,3,3,4,Help with editor modes 9 | 8,architect,industry,20,10,no,"custom project-specific templates, wikis, Sharepoint, Markdown files",Professional,no,20,4,4,1,"Help with editor modes and list behavior of ""considered options""" 10 | 9,head of cloud innovation,industry,7,4,yes,"custom project-specific templates, wikis, Confluence",Professional,no,9,5,3,3,Help with editor modes 11 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import App from "./App.vue"; 3 | import Vuetify from "vuetify"; 4 | import "vuetify/dist/vuetify.min.css"; 5 | import "roboto-fontface/css/roboto/roboto-fontface.css"; 6 | import "@mdi/font/css/materialdesignicons.css"; 7 | import VueSimpleAlert from "vue-simple-alert"; 8 | import { setHeaders } from "./plugins/apiConfig/config"; 9 | 10 | import router from "./plugins/router"; 11 | 12 | Vue.use(Vuetify); 13 | 14 | /* Needed to refresh the code mirror, when it becomes visible or is resized. */ 15 | import { ObserveVisibility } from "vue-observe-visibility"; 16 | Vue.directive("observe-visibility", ObserveVisibility); 17 | import resize from "vue-element-resize-detector"; 18 | Vue.use(resize); 19 | 20 | /* Import directive v-autowidth to resize an input field to fit its content. */ 21 | import VueInputAutowidth from "vue-input-autowidth"; 22 | Vue.use(VueInputAutowidth); 23 | 24 | import VueTippy, { TippyComponent } from "vue-tippy"; 25 | Vue.use(VueTippy, { 26 | directive: "tippy", // => v-tippy 27 | flipDuration: 0, 28 | popperOptions: { 29 | modifiers: { 30 | preventOverflow: { 31 | enabled: false 32 | }, 33 | hide: { 34 | enabled: false 35 | } 36 | } 37 | } 38 | }); 39 | 40 | Vue.component("tippy", TippyComponent); 41 | 42 | Vue.use(VueSimpleAlert); 43 | 44 | Vue.config.productionTip = false; 45 | 46 | // axios interceptor for header 47 | setHeaders(); 48 | 49 | new Vue({ 50 | router, 51 | vuetify: new Vuetify(), 52 | render: (h) => h(App) 53 | }).$mount("#app"); 54 | -------------------------------------------------------------------------------- /cypress/e2e/adrManagerTest/AddRepo.cy.js: -------------------------------------------------------------------------------- 1 | import { REST_LIST_REPO_URL, TEST_BASE_URL, REST_REPO_URL } from "../../support/e2e"; 2 | 3 | context("Listing and adding repositories", () => { 4 | beforeEach(() => { 5 | window.localStorage.clear(); 6 | window.localStorage.setItem("authId", Cypress.env("OAUTH_E2E_AUTH_ID")); 7 | window.localStorage.setItem("user", Cypress.env("USER")); 8 | cy.visit(TEST_BASE_URL); 9 | cy.intercept("GET", REST_LIST_REPO_URL).as("getRepos"); 10 | cy.get("[data-cy=addRepo]").click(); 11 | cy.wait("@getRepos").its("response.statusCode").should("eq", 200); 12 | }); 13 | it("Check if at least 1 repository is displayed", () => { 14 | cy.get("[data-cy=listRepo]").should("have.length.greaterThan", 0); 15 | }); 16 | it("Add all repositories", () => { 17 | cy.get("[data-cy=listRepo]").then((listing) => { 18 | const numberOfAddedRepositories = 3; 19 | let counter = 0; 20 | // add each repo with a click 21 | cy.get("[data-cy=listRepo]").each(() => { 22 | counter++; 23 | if (counter > numberOfAddedRepositories) { 24 | return; 25 | } 26 | cy.get("[data-cy=listRepo]").eq(0).click(); 27 | }); 28 | 29 | // confirm repo adding dialog 30 | cy.get("[data-cy=addRepoDialog]").click(); 31 | cy.intercept("GET", REST_REPO_URL).as("showRepos"); 32 | cy.wait("@showRepos", { timeout: 15000 }); 33 | 34 | // check if the correct number of repos was added 35 | cy.get("[data-cy=repoNameList]").children().should("have.length", numberOfAddedRepositories); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /cypress/e2e/adrManagerTest/DeleteAdr.cy.js: -------------------------------------------------------------------------------- 1 | import { REST_REPO_URL, REST_LIST_REPO_URL, TEST_BASE_URL } from "../../support/e2e"; 2 | 3 | context("Deleting an ADR from a repo", () => { 4 | it("Remove one ADR", () => { 5 | window.localStorage.clear(); 6 | window.localStorage.setItem("authId", Cypress.env("OAUTH_E2E_AUTH_ID")); 7 | window.localStorage.setItem("user", Cypress.env("USER")); 8 | cy.visit(TEST_BASE_URL); 9 | // add the ADR-Manager repo 10 | cy.intercept("GET", REST_LIST_REPO_URL).as("getRepos"); 11 | 12 | cy.get("[data-cy=addRepo]").click(); 13 | cy.wait("@getRepos").its("response.statusCode").should("eq", 200); 14 | cy.get("[data-cy=listRepo]").contains("ADR-Manager").click(); 15 | cy.get("[data-cy=addRepoDialog]").click(); 16 | cy.intercept("GET", REST_REPO_URL).as("showRepos"); 17 | cy.wait("@showRepos", { timeout: 10000 }); 18 | cy.get("[data-cy=adrList]").then((adrList) => { 19 | // get number of ADRs in repo 20 | const adrCount = Cypress.$(adrList).length; 21 | // delete the last one 22 | cy.get("[data-cy=deleteAdrBtn]").eq(0).click(); 23 | cy.get("[data-cy=dialogDeleteAdrBtn]").click(); 24 | // check if it's gone 25 | cy.get("[data-cy=adrList]").should("have.length", adrCount - 1); 26 | cy.get("[data-cy=adrList]").should(() => { 27 | // check if localeStorage has been set accordingly 28 | const addedRepos = JSON.parse(localStorage.getItem("addedRepositories")); 29 | expect(addedRepos[0].adrs.length).to.eq(adrCount - 1); 30 | expect(addedRepos[0].deletedAdrs.length).to.eq(1); 31 | }); 32 | }); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /src/components/ToolbarMenuMode.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/components/DatePickerMenu.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 65 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "adr-manager", 3 | "version": "2.0.0", 4 | "type": "module", 5 | "scripts": { 6 | "start": "vite", 7 | "dev": "vite", 8 | "build": "vite build", 9 | "serve": "vite preview", 10 | "test": "jest", 11 | "e2e:test": "cypress run", 12 | "e2e:test-ci": "cypress run --browser chrome --headless --config video=false,screenshotOnRunFailure=false", 13 | "format": "prettier --write . --ignore-unknown" 14 | }, 15 | "dependencies": { 16 | "@mdi/font": "^7.2.96", 17 | "antlr4": "^4.13.0", 18 | "axios": "^1.4.0", 19 | "core-js": "^3.31.1", 20 | "firebase": "^10.7.0", 21 | "marked": "^5.1.1", 22 | "roboto-fontface": "*", 23 | "sanitize-filename": "^1.6.3", 24 | "splitpanes": "^2.4.1", 25 | "vue": "^2.7.14", 26 | "vue-codemirror": "^4.0.6", 27 | "vue-easy-dnd": "^1.22.0", 28 | "vue-element-resize-detector": "^1.0.6", 29 | "vue-input-autowidth": "^1.0.11", 30 | "vue-observe-visibility": "^1.0.0", 31 | "vue-resource": "^1.5.3", 32 | "vue-router": "^3.6.5", 33 | "vue-simple-alert": "^1.1.1", 34 | "vue-tippy": "^4.16.1", 35 | "vuetify": "^2.7.0" 36 | }, 37 | "devDependencies": { 38 | "@babel/core": "^7.22.8", 39 | "@babel/preset-env": "^7.22.7", 40 | "@cypress/code-coverage": "^3.11.0", 41 | "babel-jest": "^29.6.1", 42 | "cypress": "^12.17.1", 43 | "cypress-vite": "^1.4.1", 44 | "jest": "^29.6.1", 45 | "prettier": "3.0.0", 46 | "sass": "~1.32", 47 | "unplugin-vue-components": "^0.25.1", 48 | "vite": "^4.4.3", 49 | "vite-plugin-vue2": "^2.0.3", 50 | "vue-template-compiler": "^2.7.14" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/views/LandingPage.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 48 | -------------------------------------------------------------------------------- /src/components/ConnectToGitHubButton.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 55 | -------------------------------------------------------------------------------- /docs/evaluation/user-study/material-during-study/interview-guide.md: -------------------------------------------------------------------------------- 1 | # Interview Guide 2 | 3 | The following sections contain the basic agenda for our semi-structured interviews. 4 | We will loosely follow these questions based on the participant's responses. 5 | 6 | 7 | ## Demographic and Professional Background 8 | 9 | * Role (e.g. (lead) developer, (lead) architect, data engineer, etc.) 10 | * General domain (e.g. web, frontend, backend, data science, something completely different) 11 | * Years of professional experience 12 | * Experience with ADRs (any document containing ADs) 13 | * For how long have you used ADRs? 14 | * What was the average size of the teams you used ADRs with? 15 | * What formats do you use for ADRs? 16 | * How standardized are the formats? Are there templates? 17 | * Do you have experience with [MADRs](https://github.com/adr/madr)? 18 | * Which tools do you use to manage ADRs? (e.g. adr-tools + GitHub, local text editor like VSC, IntelliJ, Confluence, PowerPoint, etc.) 19 | 20 | 21 | 22 | ## Functional Suitability 23 | 24 | * Which provided functionality needs to be improved? 25 | * Which additional functionality would you need to consider using the tool in your professional work? 26 | 27 | ## Usability 28 | 29 | * How understandable was the user interface for you? 30 | * How efficiently usable was the user interface for you? 31 | * Which modes would you use most often? Would you switch between modes? 32 | 33 | 34 | ## Final Verdict 35 | 36 | Please indicate your (dis)agreement to the following statements with these 5 labels: 37 | `strongly disagree`, `disagree`, `neutral`, `agree`, `strongly agree` 38 | 39 | * The functional suitability of the tool is fitting for its purpose. 40 | * The usability of the tool allows its convenient usage. 41 | * It is likely that I will use the tool in the future. 42 | 43 | -------------------------------------------------------------------------------- /src/components/EditorRaw.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 70 | 71 | 81 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | // For a detailed explanation regarding each configuration property, visit: 2 | // https://jestjs.io/docs/en/configuration.html 3 | export default { 4 | // Automatically clear mock calls and instances between every test 5 | clearMocks: true, 6 | // Indicates whether the coverage information should be collected while executing the test 7 | collectCoverage: false, 8 | // An array of glob patterns indicating a set of files for which coverage information should be collected 9 | collectCoverageFrom: ["./src/**/*.js"], 10 | // The directory where Jest should output its coverage files 11 | coverageDirectory: "coverage", 12 | // An array of regexp pattern strings used to skip coverage collection 13 | coveragePathIgnorePatterns: ["/node_modules/", "/plugins/parser/", "/tests/"], 14 | // A list of reporter names that Jest uses when writing coverage reports 15 | coverageReporters: ["json", "text", "lcov", "clover"], 16 | // An array of directory names to be searched recursively up from the requiring module's location 17 | moduleDirectories: ["node_modules"], 18 | // An array of file extensions your modules use 19 | moduleFileExtensions: ["js", "json", "vue", "node"], 20 | // A map from regular expressions to module names that allow to stub out resources with a single module 21 | moduleNameMapper: { 22 | vue$: "vue/dist/vue.common.js", 23 | "^@/(.*)$": "/src/$1" 24 | }, 25 | // The test environment that will be used for testing 26 | testEnvironment: "node", 27 | // The glob patterns Jest uses to detect test files 28 | testMatch: ["**/__tests__/**/*.js?(x)", "**/?(*.)+(spec|test).js?(x)"], 29 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped 30 | testPathIgnorePatterns: ["/node_modules/"], 31 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation 32 | transformIgnorePatterns: ["/node_modules/(?!three)"] 33 | }; 34 | -------------------------------------------------------------------------------- /docs/adr/0002-use-antlr-for-parsing-adrs.md: -------------------------------------------------------------------------------- 1 | # Use ANTLR for parsing ADRs 2 | 3 | * Status: accepted 4 | 5 | Technical Story: Proposed in the GitHub Issues [#17](https://github.com/koppor/adr-manager/issues/17), [#19](https://github.com/koppor/adr-manager/issues/19) 6 | 7 | ## Context and Problem Statement 8 | 9 | ADR Manager needs to parse existing ADRs. Each ADR is stored in a markdown file. Example: https://github.com/adr/madr/blob/master/docs/adr/0001-use-CC0-as-license.md. 10 | 11 | ## Considered Options 12 | 13 | * Use a generic Markdown Parser 14 | * Use ANTLR to create an ADR specific parser 15 | * Write a parser from scratch 16 | 17 | ## Decision Outcome 18 | 19 | Chosen option: "Use ANTLR to create an ADR specific parser", because comes out best (see below). 20 | 21 | ### Positive Consequences 22 | 23 | * Some existing ADRs are accepted now without changes. 24 | * Better control over output 25 | 26 | ### Negative Consequences 27 | 28 | * The new parser is worse at parsing "invalid" ADRs than the generic one. 29 | * ANTLR Parser seems to be quite slow. 30 | 31 | ## Pros and Cons of the Options 32 | 33 | ### Use a generic Markdown Parser 34 | 35 | Enhance generic Markdown parser and use the result of that parser. For that, available options are listed at https://github.com/f43i4n/md-schema/blob/master/docs/related-work.md. 36 | 37 | This option was used in the mock-up with the library [md-to-json](https://github.com/ajithr/md-2-json). 38 | 39 | * Good, because easiest solution. 40 | * Good, because md-to-json can parse the json back to a Markdown. 41 | * Bad, because md-to-json adds extra line breaks, which are hard to remove. 42 | 43 | ### Use ANTLR to create an ADR specific parser 44 | 45 | Use ANTLR4 to write a parser specifically for ADRs. 46 | 47 | * Good, because flexible parser and full control over output. 48 | * Good, because hopefully maintainable. ANTLR Grammar can be used to specify how ADRs must look. 49 | * Bad, because extra work. 50 | 51 | ### Write a parser from scratch 52 | 53 | * Bad, because extra work. 54 | * Bad, because writing stuff from scratch is a bad anti pattern. 55 | -------------------------------------------------------------------------------- /tests/parser.test.js: -------------------------------------------------------------------------------- 1 | // Tested functionality 2 | import { md2adr, adr2md, naturalCase2snakeCase, snakeCase2naturalCase } from "../src/plugins/parser.js"; 3 | 4 | // Needed for testing 5 | import { randomStrings, MD_ParsedMADR_Pairs, validMarkdownADRs } from "./constants.js"; 6 | 7 | /** 8 | * Convergence of the parser: 9 | * The output of the parser must always be accepted by the parser. 10 | */ 11 | for (let i = 0; i < randomStrings.length; i++) { 12 | test("Test parser convergence of random strings.", () => { 13 | let result1 = adr2md(md2adr(randomStrings[i])); 14 | let result2 = adr2md(md2adr(result1)); 15 | expect(result2).toBe(result1); 16 | }); 17 | } 18 | 19 | for (let i = 0; i < MD_ParsedMADR_Pairs.length; i++) { 20 | test("Test parser convergence of possibly incorrect ADRs.", () => { 21 | let result1 = adr2md(md2adr(MD_ParsedMADR_Pairs[i].md)); 22 | let result2 = adr2md(md2adr(result1)); 23 | expect(result2).toBe(result1); 24 | }); 25 | } 26 | 27 | /** 28 | * Precision for valid ADRs: 29 | * The output of the parser should be equal to the input ADR. This only holds for valid MADRs. 30 | */ 31 | for (let i = 0; i < validMarkdownADRs.length; i++) { 32 | test("Test exact reparsing", async () => { 33 | let result = adr2md(md2adr(validMarkdownADRs[i])); 34 | expect(result).toBe(validMarkdownADRs[i]); 35 | }); 36 | } 37 | 38 | /** 39 | * Test of the function md2adr. 40 | * Compares some parsed ADRs to manually parsed ADRs. 41 | */ 42 | MD_ParsedMADR_Pairs.forEach(function (pair) { 43 | test("Test md2adr", () => { 44 | let result = md2adr(pair.md); 45 | expect(result).toStrictEqual(pair.adr); 46 | }); 47 | }); 48 | 49 | /** 50 | * Tests of the utility functions snakeCase2naturalCase and naturalCase2snakeCase 51 | */ 52 | test("Test snakeCase2naturalCase", () => { 53 | let result = snakeCase2naturalCase("0005-use-dashes-in-file-names.md"); 54 | expect(result).toBe("0005 Use Dashes In File Names.md"); 55 | }); 56 | 57 | test("Test naturalCase2snakeCase", () => { 58 | let result = naturalCase2snakeCase("0005 Use dashes in File names.md"); 59 | expect(result).toBe("0005-use-dashes-in-file-names.md"); 60 | }); 61 | -------------------------------------------------------------------------------- /src/components/DialogDeleteAdr.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/components/DialogRemoveRepository.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /cypress/e2e/adrManagerTest/Routing.cy.js: -------------------------------------------------------------------------------- 1 | import { REST_LIST_REPO_URL, TEST_BASE_URL } from "../../support/e2e"; 2 | context("Routing and correct URLs", () => { 3 | beforeEach(() => { 4 | window.localStorage.clear(); 5 | window.localStorage.setItem("authId", Cypress.env("OAUTH_E2E_AUTH_ID")); 6 | window.localStorage.setItem("user", Cypress.env("USER")); 7 | cy.visit(TEST_BASE_URL); 8 | }); 9 | it("URL corresponds to opened repo and ADR", () => { 10 | cy.url().should("equal", TEST_BASE_URL); 11 | 12 | // add the ADR-Manager repo 13 | cy.intercept("GET", REST_LIST_REPO_URL).as("getRepos"); 14 | cy.get("[data-cy=addRepo]").click(); 15 | cy.wait("@getRepos").its("response.statusCode").should("eq", 200); 16 | cy.get("[data-cy=listRepo]").contains("ADR-Manager").click(); 17 | cy.get("[data-cy=addRepoDialog]").click(); 18 | 19 | // URL should contain full name, branch, and ADR name 20 | cy.url().should( 21 | "equal", 22 | `${TEST_BASE_URL}/adr/adr-manager/main/0000-use-markdown-architectural-decision-records.md` 23 | ); 24 | cy.get("[data-cy=adrList]").then((adrList) => { 25 | // get number of ADRs in repo 26 | const adrCount = Cypress.$(adrList).length; 27 | expect(adrCount).to.be.greaterThan(3); 28 | // add new ADR, it should be opened automatically 29 | cy.get("[data-cy=newADR]").click({ force: true }); 30 | // with an opened ADR, the URL should contain full name, branch, and new ADR name 31 | cy.url().should("equal", `${TEST_BASE_URL}/adr/adr-manager/main/${String(adrCount).padStart(4, "0")}-.md`); 32 | 33 | // remove all ADRs 34 | cy.get("[data-cy=deleteAdrBtn]").each(($el) => { 35 | cy.wrap($el).click(); 36 | cy.get("[data-cy=dialogDeleteAdrBtn]").click(); 37 | }); 38 | 39 | // URL should contain full name and branch 40 | cy.url().should("equal", `${TEST_BASE_URL}/adr/adr-manager/main`); 41 | }); 42 | }); 43 | 44 | it("Redirect to /login if localStorage does not contain the authId", () => { 45 | cy.url().should("contain", TEST_BASE_URL); 46 | cy.clearLocalStorage(); 47 | cy.reload(); 48 | cy.url().should("contain", "#/login"); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /docs/evaluation/user-study/material-during-study/meeting-summary.md: -------------------------------------------------------------------------------- 1 | **Meeting protocol - 12.01.2021, 14:00 to 17:30** 2 | 3 | **Recorder**: Anna Mustermann 4 | 5 | 6 | 7 | | **name** | **present** | 8 | | --------------- | ----------- | 9 | | Anna Mustermann | yes | 10 | | Bob Musterfrau | yes | 11 | | Marco Mayer | yes | 12 | | Matt Müller | no | 13 | 14 | ## Meeting Topic 15 | 16 | Using an end-to-end (e2e) testing framework 17 | 18 | **Context** 19 | 20 | We need an e2e test framework for our application. It needs to support JavaScript. Which test framework can we use? 21 | 22 | **Options** 23 | 24 | - Anna proposes the use of Cypress tests 25 | - Bob is more in favor of using Selenium tests 26 | - Marco is against e2e tests 27 | 28 | **Discussion** 29 | 30 | - Cypress 31 | - Advantages 32 | - Cypress runs directly in the browser and is fast 33 | - It can be installed quickly and no configuration is required 34 | - Tests can be debugged easily 35 | - No previous knowledge is required 36 | - Captures snapshots at the time of test execution 37 | - Disadvantages 38 | - Does not support multi-tabs 39 | - Limited support for iFrames 40 | - Supports only JavaScript to create test cases 41 | 42 | - Selenium 43 | - Advantages 44 | - Compatible with the latest browsers 45 | - Multiple browser instances 46 | - Mobile tests 47 | - Disadvantages 48 | - The setup is difficult 49 | - Execution speed is slow 50 | - Time travel is not available 51 | 52 | - No e2e tests 53 | - Advantages 54 | - Time saving 55 | - Disadvantages 56 | - Test coverage is not increased 57 | - Bugs can remain undetected 58 | 59 | **Outcome**: 60 | 61 | - Cypress was selected as the framework for e2e testing 62 | - Positive consequences 63 | - Good coverage of code through Cypress e2e tests 64 | - Fast implementation 65 | - Errors can be easily debugged 66 | - Create screenshots and videos in the pipeline 67 | - Negative consequences 68 | - Time-consuming execution of the pipeline 69 | - Time-consuming adaptation of tests when changes are made to the code 70 | - Links 71 | - https://www.cypress.io 72 | 73 | **Next recorder with expected date**: Bob Musterfrau, next meeting: 22.01.2021, 2 pm 74 | -------------------------------------------------------------------------------- /cypress/e2e/adrManagerTest/Parser.cy.js: -------------------------------------------------------------------------------- 1 | import { REST_LIST_REPO_URL, TEST_BASE_URL } from "../../support/e2e"; 2 | 3 | context("Using Markdown modes", () => { 4 | it("Convert raw Markdown", () => { 5 | window.localStorage.clear(); 6 | window.localStorage.setItem("authId", Cypress.env("OAUTH_E2E_AUTH_ID")); 7 | window.localStorage.setItem("user", Cypress.env("USER")); 8 | cy.visit(TEST_BASE_URL); 9 | 10 | // add ADR Manager repo 11 | cy.intercept("GET", REST_LIST_REPO_URL).as("getRepos"); 12 | cy.get("[data-cy=addRepo]").click(); 13 | cy.wait("@getRepos").its("response.statusCode").should("eq", 200); 14 | cy.get("[data-cy=listRepo]").contains("ADR-Manager").click(); 15 | cy.get("[data-cy=addRepoDialog]").click(); 16 | cy.get("[data-cy=repoNameList]").click(); 17 | 18 | // add a new ADR 19 | cy.get("[data-cy=repoNameList]").click(); 20 | cy.get("[data-cy=newADR]").click(); 21 | 22 | // switch to raw Markdown mode 23 | cy.contains(" Raw Markdown ").click(); 24 | // clear existing Markdown in new ADR 25 | cy.get("[data-cy=markdownText]").click().type("{ctrl+a}{del}"); 26 | // input Markdown that doesn't correspond to the MADR template 27 | cy.get("[data-cy=markdownText]") 28 | .click() 29 | .type( 30 | "# ADR-Manager Test\n> All artefacts related to a research project to propose a tool-supported approach for the efficient creation and management of [architectural decision records (ADRs)](https://adr.github.io) via a graphical user interface (GUI)\n## Developer Instructions" 31 | ); 32 | // navigate to conversion view 33 | cy.contains(" Convert ").click(); 34 | // check for some required UI elements 35 | cy.get("[data-cy=convertEditor]").should(($editor) => { 36 | expect($editor).to.contain("https://github.com/adr/madr/blob/master/template/template.md"); 37 | expect($editor).to.contain("Your ADR"); 38 | expect($editor).to.contain("Result"); 39 | }); 40 | // accept the conversion 41 | cy.get("[data-cy=acceptDiv]").click(); 42 | 43 | // check title 44 | cy.get("[data-cy=titleAdr]").should("have.value", "ADR-Manager Test"); 45 | // switch to Markdown preview mode 46 | cy.contains(" Markdown Preview ").click(); 47 | // check title 48 | cy.get("[data-cy=markdownPreview]").should("contain", "ADR-Manager Test"); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /docs/evaluation/user-study/results/analysis-script.R: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Results Evaluation and Visualization 3 | ######################################################################## 4 | 5 | # Delete current environment variables -------- 6 | rm(list = ls(all.names = TRUE)) 7 | 8 | # Load required packages ------- 9 | library(dplyr) 10 | library(likert) 11 | 12 | likertLevels <- c("1", "2", "3", "4", "5") 13 | likertLabels <- c("strongly disagree (1) ", "disagree (2) ", "neutral (3) ", "agree (4) ", "strongly agree (5) ") 14 | 15 | # Read data ------------ 16 | data <- read.csv("docs/evaluation/user-study/analysis/participants.csv", sep = ",") 17 | 18 | str(data) 19 | summary(data) 20 | 21 | # Median participant stats 22 | data %>% 23 | summarise( 24 | numParticipants = n(), 25 | mdYearsOfExperience = median(Years.of.Experience, na.rm = TRUE), 26 | mdYearsofAdrExperience = median(Years.of.ADR.Experience, na.rm = TRUE), 27 | percentageMadrUsage = sum(Has.Used.MADR == "yes") / n(), 28 | mdTaskDuration = median(Task.Duration.in.min, na.rm = TRUE), 29 | mdFunctionalSuitability = median(Functional.Suitability.Likert, na.rm = TRUE), 30 | mdUsability = median(Usability.Likert, na.rm = TRUE), 31 | mdFutureUsage = median(Future.Usage.Likert, na.rm = TRUE) 32 | ) 33 | 34 | # Distribution of roles 35 | data %>% select(Role) %>% table 36 | 37 | # Likert plots -------- 38 | 39 | data %>% 40 | select(Functional.Suitability.Likert, Usability.Likert, Future.Usage.Likert) %>% 41 | 42 | mutate(Functional.Suitability.Likert = factor(Functional.Suitability.Likert, levels = likertLevels, ordered = TRUE, labels = likertLabels)) %>% 43 | rename("The functional suitability of the tool is fitting for its purpose." = Functional.Suitability.Likert) %>% 44 | 45 | mutate(Usability.Likert = factor(Usability.Likert, levels = likertLevels, ordered = TRUE, labels = likertLabels)) %>% 46 | rename("The usability of the tool allows its convenient usage." = Usability.Likert) %>% 47 | 48 | mutate(Future.Usage.Likert = factor(Future.Usage.Likert, levels = likertLevels, ordered = TRUE, labels = likertLabels)) %>% 49 | rename("It is likely that I will use the tool in the future." = Future.Usage.Likert) %>% 50 | 51 | likert() %>% 52 | plot(type = "bar", text.size = 4.5, wrap = 30) + 53 | theme( 54 | text = element_text(size = 16, face = "bold"), 55 | legend.title = element_text(size = 0, color = "white"), 56 | legend.text = element_text(size = 12), 57 | legend.position = "bottom" 58 | ) 59 | -------------------------------------------------------------------------------- /cypress/e2e/adrManagerTest/AddNewAdr.cy.js: -------------------------------------------------------------------------------- 1 | import { TEST_BASE_URL, REST_LIST_REPO_URL } from "../../support/e2e"; 2 | 3 | context("Adding a new ADR to a repo", () => { 4 | it("Create a new ADR", () => { 5 | window.localStorage.clear(); 6 | window.localStorage.setItem("authId", Cypress.env("OAUTH_E2E_AUTH_ID")); 7 | window.localStorage.setItem("user", Cypress.env("USER")); 8 | cy.visit(TEST_BASE_URL); 9 | // add the ADR-Manager repo 10 | cy.intercept("GET", REST_LIST_REPO_URL).as("getRepos"); 11 | cy.get("[data-cy=addRepo]").click(); 12 | cy.wait("@getRepos").its("response.statusCode").should("eq", 200); 13 | cy.get("[data-cy=listRepo]").contains("ADR-Manager").click(); 14 | 15 | cy.get("[data-cy=addRepoDialog]").click(); 16 | cy.get("[data-cy=repoNameList]").click(); 17 | cy.get("[data-cy=adrList]").then((adrList) => { 18 | // get number of ADRs in repo 19 | const adrCount = Cypress.$(adrList).length; 20 | // add new ADR 21 | cy.get("[data-cy=newADR]").click({ force: true }); 22 | // length should be adrCount + 1 23 | cy.get("[data-cy=adrList]").should("have.length", adrCount + 1); 24 | cy.get("[data-cy=adrList]").should(() => { 25 | expect(localStorage.getItem("addedRepositories")).to.not.eq("[]"); 26 | }); 27 | 28 | // add title to new ADR 29 | cy.get("[data-cy=titleAdr]").click(); 30 | cy.get("[data-cy=titleAdr]").type("TestTitle"); 31 | 32 | // add context to new ADR 33 | cy.get("[data-cy=contextAdr]").click(); 34 | cy.get("[data-cy=contextAdr] textarea").eq(1).type("ContextAdr", { 35 | force: true 36 | }); 37 | 38 | // add considered options text to new ADR and test the add btn 39 | cy.get("[data-cy=considerOptTextAdr]").children().eq(1).type("Con. Opt 1)").should("have.length", 1); 40 | cy.get("[data-cy=considerOptTextAdr]").children().should("have.length", 2); 41 | 42 | // choose decision outcome 43 | cy.get("[data-cy=decOutChooseAdr]").click(); 44 | cy.get(".v-list-item__title").contains("Con. Opt 1)").click(); 45 | cy.get("[data-cy=checkConsOptAdr]").should("be.visible"); 46 | 47 | // add because to decision outcome 48 | cy.get("[data-cy=decOutBecAdr]").click(); 49 | cy.get("[data-cy=decOutBecAdr]").type("it has to be"); 50 | }); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | lerna-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 13 | 14 | # Runtime data 15 | pids 16 | *.pid 17 | *.seed 18 | *.pid.lock 19 | 20 | # Directory for instrumented libs generated by jscoverage/JSCover 21 | lib-cov 22 | 23 | # Coverage directory used by tools like istanbul 24 | coverage 25 | *.lcov 26 | 27 | # nyc test coverage 28 | .nyc_output 29 | 30 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 31 | .grunt 32 | 33 | # Bower dependency directory (https://bower.io/) 34 | bower_components 35 | 36 | # cypress 37 | cypress.env.json 38 | cypress/downloads 39 | 40 | # node-waf configuration 41 | .lock-wscript 42 | 43 | # Compiled binary addons (https://nodejs.org/api/addons.html) 44 | build/Release 45 | 46 | # Cypress videos 47 | cypress/videos/** 48 | cypress/screenshots/** 49 | 50 | # Dependency directories 51 | node_modules/ 52 | jspm_packages/ 53 | 54 | # TypeScript v1 declaration files 55 | typings/ 56 | 57 | # TypeScript cache 58 | *.tsbuildinfo 59 | 60 | # Optional npm cache directory 61 | .npm 62 | 63 | # Optional eslint cache 64 | .eslintcache 65 | 66 | # Microbundle cache 67 | .rpt2_cache/ 68 | .rts2_cache_cjs/ 69 | .rts2_cache_es/ 70 | .rts2_cache_umd/ 71 | 72 | # Optional REPL history 73 | .node_repl_history 74 | 75 | # Output of 'npm pack' 76 | *.tgz 77 | 78 | # Yarn Integrity file 79 | .yarn-integrity 80 | 81 | # dotenv environment variables file 82 | .env 83 | .env.test 84 | 85 | # parcel-bundler cache (https://parceljs.org/) 86 | .cache 87 | 88 | # Next.js build output 89 | .next 90 | 91 | # Nuxt.js build / generate output 92 | .nuxt 93 | dist 94 | 95 | # Gatsby files 96 | .cache/ 97 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 98 | # https://nextjs.org/blog/next-9-1#public-directory-support 99 | # public 100 | 101 | # vuepress build output 102 | .vuepress/dist 103 | 104 | # Serverless directories 105 | .serverless/ 106 | 107 | # FuseBox cache 108 | .fusebox/ 109 | 110 | # DynamoDB Local files 111 | .dynamodb/ 112 | 113 | # TernJS port file 114 | .tern-port 115 | 116 | # Fody - auto-generated XML schema 117 | FodyWeavers.xsd 118 | 119 | # IDE - VSCode 120 | .vscode/* 121 | 122 | # Visual Studio 2015/2017 cache/options directory 123 | .vs/ 124 | 125 | !.vscode/tasks.json 126 | !.vscode/launch.json 127 | !.vscode/extensions.json 128 | .history/* 129 | 130 | # System Files 131 | .DS_Store 132 | Thumbs.db 133 | ehthumbs.db 134 | *.orig 135 | *.bak 136 | *.zip 137 | 138 | # Antlr files 139 | .antlr 140 | *.interp 141 | *.tokens 142 | -------------------------------------------------------------------------------- /docs/adr/0004-implement-a-global-store.md: -------------------------------------------------------------------------------- 1 | # Implement a global store 2 | 3 | * Status: accepted 4 | 5 | Technical Story: Implement a simple global store 6 | 7 | ## Context and Problem Statement 8 | 9 | The ADR-Manager has a state that needs to be stored. 10 | Data that make sense storing globally: 11 | 12 | * Edited ADRs until they're committed (maybe even all ADRs from all added repositories) 13 | * Added Repositories 14 | * The current mode (basic, advanced, professional) 15 | 16 | How should the state be stored? 17 | 18 | ## Decision Drivers 19 | 20 | * ADR-Manager should be lightweight and easy to implement. 21 | * ADR-Manager should be easy to maintain. 22 | 23 | ## Considered Options 24 | 25 | * Don't use any global store 26 | * Only use local storage in combination with Events/Props 27 | * Use the Vue-State-Manager Vuex 28 | * Implement a state manager from scratch. 29 | 30 | ## Decision Outcome 31 | 32 | Chosen option: "Implement a state manager from scratch.", because comes out best. The data can additionally be stored in Local Storage but this should be managed by the global store as well. 33 | 34 | ### Positive Consequences 35 | 36 | * New functionality will be easier to add. 37 | 38 | ## Pros and Cons of the Options 39 | 40 | ### Don't use any global store 41 | 42 | Just "cascade" updates between Vue-Components via Events and Props. E.g. each editor tab has a prop (v-model) for the displayed ADR. Whenever the ADR is changed the Sup-Component (currently TheEditor.vue) updates the ADR in each tab. When a new ADR is created via a toolbar menu, the event needs to cascade down to each related Editor-Component. 43 | 44 | * Good, because it's easy to implement. 45 | * Bad, because it's hard to debug. 46 | * Bad, because GUI and functionality is more directly connected. Changes to the GUI often require bigger updates to functionality and vice versa. 47 | 48 | ### Only use local storage in combination with Events/Props 49 | 50 | Use local storage (i.e. persistent storage) to store the state and use events (e. g. a global event bus) to communicate changes to the state. 51 | 52 | * Good, because it's easy to implement. 53 | * Good, because most data should be stored in persistent storage anyway. 54 | * Bad, because cannot take advantage of Vue's reactivity. 55 | 56 | ### Use the Vue-State-Manager Vuex 57 | 58 | Docs can be found at https://vuex.vuejs.org/. 59 | 60 | * Good, because best long-term maintainability. 61 | * Good, because prepares for extensions like 'Undo-Redo'. 62 | * Good, because the development team can gather experience with Vuex. 63 | * Bad, because of more concepts and boilerplate. 64 | * Bad, because does not fit in our project. We assume that ADR-Manager is a small-to-medium project and not a medium-to-large project 65 | 66 | ### Implement a state manager from scratch. 67 | 68 | Implement a state manager from scratch as described at https://vuejs.org/v2/guide/state-management.html#Simple-State-Management-from-Scratch. 69 | 70 | * Good, because GUI and functionality are split better. Debugging is easier. 71 | * Good, because it's a compromise between using Vuex and using no global state. 72 | -------------------------------------------------------------------------------- /cypress/e2e/adrManagerTest/Modes.cy.js: -------------------------------------------------------------------------------- 1 | import { TEST_BASE_URL, REST_LIST_REPO_URL } from "../../support/e2e"; 2 | 3 | context("Using editor modes", () => { 4 | it("Switch to professional mode and create a new ADR", () => { 5 | window.localStorage.clear(); 6 | window.localStorage.setItem("authId", Cypress.env("OAUTH_E2E_AUTH_ID")); 7 | window.localStorage.setItem("user", Cypress.env("USER")); 8 | cy.visit(TEST_BASE_URL); 9 | 10 | // add ADR Manager repo 11 | cy.intercept("GET", REST_LIST_REPO_URL).as("getRepos"); 12 | cy.get("[data-cy=addRepo]").click(); 13 | cy.wait("@getRepos").its("response.statusCode").should("eq", 200); 14 | cy.get("[data-cy=listRepo]").contains("ADR-Manager").click(); 15 | cy.get("[data-cy=addRepoDialog]").click(); 16 | cy.get("[data-cy=repoNameList]").click(); 17 | 18 | // add a new ADR 19 | cy.get("[data-cy=repoNameList]").click(); 20 | cy.get("[data-cy=newADR]").click(); 21 | 22 | // switch to professional mode 23 | cy.contains(" professional ").click(); 24 | 25 | // select status 26 | cy.get("[data-cy=statusPro]").click(); 27 | cy.get(".v-list-item__title").contains("rejected").click(); 28 | cy.get("[data-cy=statusPro]").should("have.class", "red red--text"); 29 | cy.get("[data-cy=statusPro]").click(); 30 | cy.get(".v-list-item__title").contains("proposed").click(); 31 | cy.get("[data-cy=statusPro]").click(); 32 | cy.get(".v-list-item__title").contains("accepted").click(); 33 | cy.get("[data-cy=statusPro]").should("have.class", "success success--text"); 34 | cy.get("[data-cy=statusPro]").click(); 35 | cy.get(".v-list-item__title").contains("deprecated").click(); 36 | cy.get("[data-cy=statusPro]").click(); 37 | cy.get(".v-list-item__title").contains("superseded").click(); 38 | 39 | // set author 40 | cy.get("[data-cy=authorPro]").click(); 41 | cy.get("[data-cy=authorPro]").type("Max"); 42 | 43 | // set technical story 44 | cy.get("[data-cy=technicalStoryPro]").click(); 45 | cy.get("[data-cy=technicalStoryPro]").type("Technical story here"); 46 | 47 | // set considered options 48 | cy.get("[data-cy=consOptPro]").type("Cons. opt."); 49 | 50 | cy.get("[data-cy=descriptionConsOpt]").type("Cons. opt. description"); 51 | cy.get("[data-cy=goodConsOpt]").type("Con. Opt 1 good").should("have.length", 1); 52 | cy.get("[data-cy=badConsOpt]").type("Con. Opt 1 bad").should("have.length", 1); 53 | 54 | // choose decision outcome 55 | cy.get("[data-cy=decOutChooseAdr]").click(); 56 | cy.get(".v-list-item__title").contains("Cons. opt.").click(); 57 | 58 | // write consequences 59 | cy.get("[data-cy=posConseqPro]").type("Con. Opt 1 positive").should("have.length", 1); 60 | cy.get("[data-cy=negConseqPro]").type("Con. Opt 1 negative").should("have.length", 1); 61 | 62 | // write Link 63 | cy.get("[data-cy=linkPro]").type("https://test.com").should("have.length", 1); 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /src/components/EditorMadrDecisionOutcome.vue: -------------------------------------------------------------------------------- 1 | 59 | 60 | 87 | -------------------------------------------------------------------------------- /docs/architecture/FMC-Diagram.drawio: -------------------------------------------------------------------------------- 1 | 7V1dc+K6Gf41zLQXYfwNXG5Ctr3YzMlkd3raq47AAnRiLI5tAumvr2TLNpZkW2DL0AZ2ZhdkWbLf99H7Le3Iftoe/xaB3eYF+zAYWYZ/HNnzkWWZtueQf2jLZ9Yy8eysYR0hn3UqG36i/0DWaLDWPfJhXOmYYBwkaFdtXOIwhMuk0gaiCB+q3VY4qM66A2soNPxcgkBs/R35yYa1mq5RXvg7ROsNm3qSX1iA5fs6wvuQzTey7O/pJ7u8BflYrH+8AT4+nDTZzyP7KcI4yb5tj08woLTNyZbd973mavHcEQwTlRus+SEObBzNXx4eJqG32P57/vwwyUb5AMGe0eMJ43dEGGIZOCJ//cCEVOTfnwmOKCWzV0k+c/KlBIB0CnNkPx42KIE/d2BJrx4IYEjbJtkG7PIHjBJExvsWoHVI2hJMO6xQEDzhgExHR7RXLv1D2uMkwu/w5IqXfugdOExO2rMPaWcvQ2aBx1oqmQXtCaYh3sIk+iRd2A0zx81uYXA2c/YdTsDhsbbNCS4KQAMGyHUxdskU8oXx5QwemZbAJLoMu7GiD1J5HKk8CaksCakcXZSyHIFSzz4hg0/avs3f4psjmTVRJJmri2TOVCDZyPICMu2jjz7I1zX9+vsGhlQi7GCIwjV9jjAjKZXWG5CM6DNndy2i/Ka8hTzXyVCS0ZcgDDEdYwEzasYpx8jlP/dUOj7uYLQisj/4LJtG1lO3SZMNilO1st3hkLKPSmi8D/zsy6Hr6PRVfLRaQYKNJUxfLjnAlIzZRRyhNQpT4dphoowHIPTLcffJbk9fB6+KtpSmUbeZwCpJh6BjZSBAyVj57g4T/0pfYZ+9wDJFHlgu4Y6+43IDwnWprTrMskJHuuZQXGVNRl9Usu0NUGj8Aoux+mSc1CGWwI5+JaCH7WKntDN+2ycBCiFr90H0/hu5CyVUmBhjg+rMqkDrQWK5blViuVNRYlm2RGKZ2kSWK+pDgcQw9L9R45D8WgYgjtGyStTCGDNkhoaRfirkpL+gTyxHNgcMFvjwXDY8pg3kQm7jFOSndzUTnzw53kdLqGADJCBaw6ShoyXn5gm33Ab1EsEAJOij+rwyBrIZXjFKJWeu3mxOvfEYyN6T3XVqq/IDTbmBDG6gjA7CQITn4POk2452iNUf2JxVbGjyJRuxBGtB08vx68lULodfuuB3tcuX+UJgkXcvfBXVZW0brrCMPUOGDG3LWFzF1FbD0T8QPIw/9k32rdEuNftzKmIqY8P1I04SvM0lRtb2xuiUiYqELB0csp8BWMDgFceItS0JY4gCK32gH1yHLfL9VJAA5hwJd1S9JoKJZI4iAoPs/iCJVCV+AyobJcfAAPFULNPvKKBa+fm4I5yjFoJMA9NOeY/u0FLyYmH6kQHO9uyZ7Wvh1TEXu+1OxlTCSk/bUncFiuMo2eA1JkbWD0zpl1L2D5gknyxEBPYJrtK9Uf1WDZ/LlHGjtG5V0baihnZtOfM66t4JU12FocbHI2pUZl9azZoKPD5lWMnvU64t99FHwbQLIKFoxmnnvavK/F54f66VU5hhDBqOyUUKz+uvxyqyxdgNDTeIMZtGMaAPQzLPSqc0UTb4J426wBhz2uDBZITWi7kcM4Vl7TZjju9vNWFOuJu72am+DF6tYqhF6OVcOgHtL4yDBaDBCGaavMBw36stq2BayE0UbQaHY3PMlgXNZWHNqbYYgS0w5mkf0bekQUSycMuocANv+goKX0JTk8O0PZPQVBZ4sbXZ40YD2LOMxFXAPhisHUVUa8tveKKOjGFAs6KDKklpcI00EoFXXRYa9aOnqB7rrO1CPTrW1KsuNO8S/SiqJH798rKupxDWlJtmco7efKi++oPFPaM+zTkRoSyAuHsQ7JJVzxm/kqSmLY1368tqikn6OQIBXnfMZ/ZlVvgg3pyXcmhARGMAamjCzwTCf/Op1fAGdzRwh6O0UILjQWuAj6dicaEqb9MU0fa4pmU34wUgUnYcpQUwJeMIueYhDkum5RUs6RCZLLeoxEYEFSnxXUlodLaaUPbVhyyFiKQOXVzH/lwXu+OpffrhhLZENZtDquY8kVCppNluERW2T3mS8o6U6yNFkr8cGCliXJuYaDCBNe7IHSU6UGLOrLEzO/lMbkueOKIDe0fJHSXc409NAQjX9vxUI6iSFA1hVfT5TzrVeOLmv/9VTE1+zI+VX5/s10i3tzlhluBAyZ1zfUCbC1TYTnOwVehvtyQEZo3j60kITETr+ymCIJWAITy0Bu8uKBLIZdJVYk0KxbQy2aIv3CfSX6D2dTx0AY6uSKuiT8VV1BZvFrW1pFYhq2+Rlyj82sDs8v9FfcJMzr/r8UeGZYE/L1mxZxOb5OWp2R0vwGfcu7Au9HZlVw0/5dk3yWoctqY9f54WboPonVg24WsEP4qSs6/GObeddbYxnjkDcs+xVLj3mle0z9Fq1aHs+y84TDOCuwAQZeX/9etB4MLUrb6yP1ug7f+wTzNqd01Ua8DzsoZWJ8STM1yvDzLhNs7ZQxQNec3+r7zorMqzq8KnR3Tk/kIrOrKtBEPDYzrh/JumPGVf8HDFutN2eNyQPOkRHo5qhaJVExHTCw+Pr1lmG0D0lhxeUrJ6QxWGvSuOLAYxNO9dbrOPbTRHo/j+eahEryhR2LTzVVWPcnS0Trbk1SjG2HImfLnqRVvTzq+i4eSP6TZi0DR4gWU2CKzydv2FNY5C2O4EOCyRcwqxSk7H4kEnqfboR0FNFUHk1Ngvw2xxdM0q3x0+/qq6xdHljgJQrQ/rCydTMWSZ+9RdPNuRip/ackjCtIbEbS6ptv1LU6dR1rdsRlIV8xLvUUHyswVciv3OW2FUV2zOptYVO+2aFOvGPLEyh0Z5R5YHthS88nAOGRjtYtgOeBDvsmOJVuiYxnCqzJcH48/eHbpgu0/FrHgfC47TZY4kDDhopXlekXtfb5dsMJz2YsWLJhIvlvliYeXN/V7LQLo1n2ghgR0a/9FUTjuQ5qs59eBqim8mZlIewfL9mR43cy1a3YxVMLtbBTKqKFsFNXmJrvsuBLvaVZIuZ0esuP2KLSU0/MFfXkuMw20aXU+IYyY1lJiVFC7ibAuh8TGYrVSUKPLmUgBXSYOxJBylEWXSoA8RXZPluJqpNPuKplKrbMmh3C6tDC1CqE0GqZpKJg83vpJUs6mU17t+KXS1g0Y1VNVLIlasxuRKBW0+Fa8ZFKYhiSjeUaGMin4Mn3PtFUGUtEW43cb+mkwQpdNIC6OE1eUYb+rHZfp+uu+/uoOPnZ+ZHRF7Yf3PVwoYWVy2xJY4qoNaQaZxZtHQ1xBJpqHqjFl6nLECGFx94WCa6u6id4KFLh9dOJNIz9kIJr+pwmjReWZjfz06r+BGs9J7g3/uYUwf9hVGWxTHVM6fcfpyJdlRasyb0XOFwjpb1WUacgg9JzmCZ1g1l6+TuzyrrqAbN705U3qQgqWCKNpsafF8gxsTKTdpOvNYyN2y65nOs7tMqT/Po72c2qjZrNjVRuJTAIpHoIsnKxpG5UAMq3oehsnnx2pik/VCT91qJz/L/+8o617+p1L2838B -------------------------------------------------------------------------------- /docs/evaluation/adr-parsing/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Parser test 3 | 4 | ## Manual test 5 | 6 | The results of the manual check are included in [adr-parsing-manual-results.csv](./adr-parsing-manual-results.csv). 7 | 8 | ## Automatic test 9 | 10 | An automatic test checking whether an ADR can be parsed was implemented. 11 | 12 | ### Prerequisites 13 | 14 | 1. The repositories that are checked are defined in the list `REPO_NAMES` in `cypress/integration/EmpriricalAnalysis.js`. Your GitHub account must have access to those. 15 | 2. Add a file `cypress.env.json` in this folder with the auth ID of an active session. While running the ADR-Manager, you can find your `authId` in the local storage. 16 | The file content of `cypress.env.json` should look like 17 | 18 | ```[JavaScript] 19 | { 20 | "OAUTH_E2E_AUTH_ID": "abcdefg-123456" 21 | } 22 | ``` 23 | 24 | where `abcdefg-123456` is your `authId`. 25 | 26 | ### Execute tests 27 | 28 | 1. Start the ADR-Manager locally: In the `adr-manager` directory execute: 29 | 30 | ```[bash] 31 | npm run serve 32 | ``` 33 | 34 | 2. Run the tests: In this directory (i.e. the same as this `README`, `cypress.json` and `cypress.env.json`) execute one of the following commands 35 | 36 | ```[bash] 37 | # Open the Cypress GUI to execute tests interactively 38 | npx cypress open 39 | 40 | # Run all tests in the command line 41 | npx cypress run --spec "./cypress/integration/EmpiricalAnalysis.js" 42 | ``` 43 | 44 | Possible reasons for failing tests: 45 | 46 | * The ADR-Manager is not run at `localhost:8080`. --> Start it. 47 | * The auth ID is not valid. --> Add or update `cypress.env.json`. 48 | * The repository was not found. Possible reasons I can think of: 49 | * You do not have access to a repository with a name defined in `REPO_NAMES`. --> Only include repositories you have access to. 50 | * The repository does not appear in the first tab of the "Add repositories" dialogue because e.g. are too many other repositories that were recently updated. (I didn't have problems with that but I think it will cause issues...) 51 | * A possible quick fix might be increase the maximum displayed repositories in the adr-manager's source code... 52 | * Alternatively one could modify the test to press the Next button. That would require to add a `data-cy` attribute to the Next button in the adr-manager source code 53 | and I would need to remember how to use if/else in the asynchronous setting of Cypress... 54 | 55 | The test now uses the search field. The pagination of repositories should not cause problems anymore. 56 | * The search function could be bugged. 57 | I'm having a feeling that, if someone has access to maaaany repositories, then searching will take veeeery long. 58 | This is because, while searching, all private repositories are fetched and compared to the search term before the public repositories are fetched. 59 | 60 | * The `docs/adr` folder doesn't exist, i.e., no ADR is found. --> Move ADRs into this folder. 61 | 62 | When in doubt, open the Cypress GUI and check which step fails. 63 | 64 | ### View results 65 | 66 | * `cypress/fixtures/CounterAdrsPerRepo.json`: Number of all ADRs per repository. 67 | * `cypress/fixtures/CounterDiffPerRepo.json`: Number of times the Convert tab was opened per repository. 68 | * `cypress/fixtures/Counter{Diff|Adrs}AllRepos.json`: Total numbers, i.e., sum of the above per-repository-counters. 69 | * `cypress/videos/EmpiricalAnalysis.js.mp4`: If the test was run with `npx cypress run --spec` a video is generated in `cypress/videos` where you can watch cypress clicking through the repositories. (Or possibly check what went wrong in case of failures.) 70 | -------------------------------------------------------------------------------- /docs/evaluation/user-study/results/case-descriptions/case-description-06.md: -------------------------------------------------------------------------------- 1 | # Case Description 6 2 | 3 | ## Demographic and Professional Background 4 | 5 | * Role: lead architect 6 | * Domain: IoT, cloud platforms (includes frontend, backend, data science) 7 | * Years of work experience: 10 years 8 | * ADR experience: 5-6 years 9 | * Team size: 3-7 architects + 6 reviewers 10 | * Used template: custom template 11 | * Tools: Microsoft based -> Wiki in DevOps 12 | 13 | ## Think-Aloud Protocol 14 | 15 | _Abbreviations_: In the following, each statement is started with a letter describing the context of the statement. 16 | * O = Observation: An observation by the researchers that the participant did not explicitly mention. 17 | * P = Participant: A statement of the participant. 18 | * I = Interference: The researchers helped or pointed out something that the participant had trouble with. 19 | 20 | Needed time: 11 min 21 | 22 | * O: No problems while connecting to GitHub, adding a repository, and creating an ADR. 23 | * O: Participant tries to fill in Markdown into option fields in basic mode only at the beginning. 24 | * O: Participant tries to enter advantages and disadvantages into a considered options field in basic mode. 25 | * I: Organizers inform them about modes. 26 | * O: All fields are filled in without further instructions except for the deciders (here, it was not exactly clear to them what they have to enter) 27 | * O: Date and status are filled in at the end. 28 | * O: No problems while pushing to GitHub. 29 | 30 | ## Interview 31 | 32 | ### Functional Suitability 33 | 34 | **Which provided functionality needs to be improved?** 35 | 36 | * It was not intuitive that you don't have to write Markdown in basic mode. 37 | 38 | **Which additional functionality would you need to consider using the tool in your professional work?** 39 | 40 | * Images for architecture diagrams, e.g. pictures in the repository. 41 | * Status tracking 42 | * Who made the first proposal? 43 | * What changes were made? 44 | * When was the individual status changed? 45 | * Who accepted it? 46 | 47 | ### Usability 48 | 49 | **How understandable was the user interface for you?** 50 | 51 | * Basic and advanced mode were confusing at the start. 52 | * Considered option fields were confusing. 53 | * Differences between advanced and professional were not clear. Professional would be enough. 54 | * It was not directly clear what was meant by "Deciders". 55 | 56 | **How efficiently usable was the user interface for you?** 57 | 58 | * Efficient to use. The drag and drop in the considered options supports this. 59 | * It was intuitively usable. 60 | 61 | **Which mode would you use most often?** 62 | 63 | * The participant would directly take the mode that can do the most, since a meeting usually contains all the information that is being requested there. 64 | 65 | #### Would you switch between modes? 66 | 67 | * No. 68 | 69 | ### Final Verdict 70 | 71 | | Statement | strongly disagree | disagree | neutral | agree | strongly agree | 72 | | --------------------------------------------------------------------- | :---------------: | :------: | :-----: | :---: | :------------: | 73 | | 1. The functional suitability of the tool is fitting for its purpose. | | | | | x | 74 | | 2. The usability of the tool allows its convenient usage. | | | | x | | 75 | | 3. It is likely that I will use the tool in the future. | | | x | | | 76 | 77 | Additional comments: 78 | 79 | - Missing support for images for completeness 80 | - If it was integrated in Azure DevOps, then yes, else no. 81 | -------------------------------------------------------------------------------- /docs/evaluation/user-study/results/case-descriptions/case-description-09.md: -------------------------------------------------------------------------------- 1 | # Case Description 9 2 | 3 | ## Demographic and Professional Background 4 | 5 | * Role: head of cloud innovation 6 | * Domain: IT service provider 7 | * Years of work experience: 5-10 years 8 | * ADR experience: 3-5 years 9 | * Team size: 5-15 people 10 | * Used template: Depends on project, Confluence or another Wiki format 11 | * Tools: Wikis 12 | 13 | ## Think-Aloud Protocol 14 | 15 | _Abbreviations_: In the following, each statement is started with a letter describing the context of the statement. 16 | * O = Observation: An observation by the researchers that the participant did not explicitly mention. 17 | * P = Participant: A statement of the participant. 18 | * I = Interference: The researchers helped or pointed out something that the participant had trouble with. 19 | 20 | Needed time: 9 min 21 | 22 | * O: No problems while connecting to GitHub, adding a repository, and creating a new ADR. 23 | * O: Participant copy-pastes options including pros and cons. They try to use Markdown syntax in the `Considered Options` field. 24 | * I: Organizers inform about modes. 25 | * O: Participant fills in the `Deciders`. 26 | * P: Participant is unsure what exactly the `Technical Story` is for. They guess that it refers to a issue-tracking system. 27 | * O: Participant fills in the `Positive Consequences`. 28 | * O: Participant fills in the `Negative Consequences`. 29 | * O: Participant fills in the `Decision Outcome`. 30 | * O: Participant is not sure if the information is saved. 31 | * O: Participant tries to switch branch but it is bugged. I: Organizers point out that it is bugged. 32 | * O: `Commit and Push` button is not easy to find. 33 | 34 | ## Interview 35 | 36 | ### Functional Suitability 37 | 38 | **Which provided functionality needs to be improved?** 39 | 40 | * Needed functionality is there. 41 | 42 | **Which additional functionality would you need to consider using the tool in your professional work?** 43 | 44 | * Voting Tool: In practice, the participant's team often votes on which option is chosen. Thus, the participant suggests to add a link to an online voting tool or integrate one. 45 | 46 | ### Usability 47 | 48 | **How understandable was the user interface for you?** 49 | 50 | * Understandable because participant knew the template. Otherwise, they may have needed more explanations of text fields. 51 | * `Commit and Push` button was hard to find. 52 | * It was not clear that, when pressing the `Accept` button in the `Convert` tab, information (that does not respect the format of MADR) is deleted. 53 | 54 | **How efficiently usable was the user interface for you?** 55 | 56 | * The tool is pretty efficiently usable. 57 | * Raw Markdown may be easier. 58 | * Having a structure is nice. 59 | 60 | **Which mode would you use most often?** 61 | 62 | * Professional mode. 63 | 64 | #### Would you switch between modes? 65 | 66 | * No. 67 | 68 | ### Final Verdict 69 | 70 | | Statement | strongly disagree | disagree | neutral | agree | strongly agree | 71 | | --------------------------------------------------------------------- | :---------------: | :------: | :-----: | :---: | :------------: | 72 | | 1. The functional suitability of the tool is fitting for its purpose. | | | | | x | 73 | | 2. The usability of the tool allows its convenient usage. | | | x | | | 74 | | 3. It is likely that I will use the tool in the future. | | | x | | | 75 | 76 | Additional comments: 77 | 78 | - Difference between `Advanced`and `Professional` mode was not clear. 79 | - In practice, the participant can't use the tool because they use Confluence. If they used GitHub and MADR, they would use the tool. 80 | -------------------------------------------------------------------------------- /docs/evaluation/user-study/material-before-study/callout-for-participation.md: -------------------------------------------------------------------------------- 1 | # Callout for Participation 2 | 3 | Hello! We are three motivated students planning to perform a study to evaluate a web-based tool. During our research project at the University of Stuttgart, which was supervised by Oliver Kopp and Justus Bogner, we built a tool to manage and edit Architectural Decision Records (ADRs) on GitHub. [ADRs](1) are a simple format to document decisions made during the development of a software project. Our tool uses [MADR](2), which is a Markdown template for ADRs. 4 | We are looking for software professionals to test our tool and give feedback on the practical usage. 5 | 6 | All materials for the study are provided in English. For the study itself, the participant can choose between German or English. 7 | 8 | Please sign up at for a 45 to 60 min study. 9 | You do not need to prepare anything on your side. 10 | Your email address won't be published, however, your name will appear at that DFN page. 11 | Thus, choose a pseudonym if you like. 12 | 13 | ## Purpose of this Study 14 | 15 | The ADR Manager is a web-based tool to create and edit Markdown-based ADRs [MADRs](2) on GitHub. 16 | The purpose of this study is to evaluate the functional suitability and usability of the ADR Manager to support the creation and editing of ADRs. By involving software professionals who have experience with ADRs, we hope to gain insights to further improve the tool. 17 | 18 | The goal of this study is _**NOT**_ to evaluate the abilities of participants. 19 | 20 | ## Study Process and Analysis 21 | 22 | Participants will be notified about the study and will be sent the material. 23 | The study is split into two parts: 24 | First, the participant will use the tool to solve a small written task while thinking aloud. 25 | Second, there will be a short interview about the experience. 26 | Study time is estimated between 30 to 50 minutes. 27 | We kindly ask your permission to record the screen and audio during the entire study for transcription. 28 | The written transcript will be sent back to the participant for his/her final approval. 29 | This is the last chance to remove or redact statements from the study. 30 | After that, the recording will be destroyed and the potentially redacted and approved transcript will be the sole base for our qualitative content analysis. 31 | 32 | ## Privacy Policy 33 | 34 | Participation is strictly voluntary and participants can abort the study at any time without a reason. 35 | Everything that is said in the study is treated as strictly confidential and will not be disclosed to anyone outside our research team without the participant’s permission. Results are aggregated and anonymized before publication. In no way can individual statements be traced back to a participant or company. After the study, participants also have the possibility to exclude or redact statements before the analysis. 36 | Anonymized, redacted and approved case descriptions as well as the analysis will be made publicly available on GitHub. This study is part of a research project at the University of Stuttgart. Results might also be included in the final submission for [ICSE SCORE 2021](https://conf.researchr.org/home/icse-2021/score-2021). 37 | During the study, participants will use a GitHub test account created by us. ADRs created by participants will be deleted immediately after the study. 38 | The video recordings and unapproved case descriptions will _**NOT**_ be published and will be deleted until April 30, 2021. 39 | 40 | 41 | We would be very happy about your participation! 42 | If you have any questions, feel free to contact us. 43 | 44 | Daniel Abajirov, st155165@stud.uni-stuttgart.de
45 | Katrin Bauer, st161700@stud.uni-stuttgart.de
46 | Manuel Merkel, st155131@stud.uni-stuttgart.de 47 | 48 |
49 | 50 | 1: https://adr.github.io 51 | 2: https://adr.github.io/madr 52 | -------------------------------------------------------------------------------- /src/components/EditorMadrCodemirror.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 124 | 125 | 132 | -------------------------------------------------------------------------------- /src/plugins/router.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import VueRouter from "vue-router"; 3 | import LandingPage from "../views/LandingPage.vue"; 4 | import EditorView from "../views/EditorView.vue"; 5 | import ErrorPage from "../views/ErrorPage.vue"; 6 | Vue.use(VueRouter); 7 | 8 | const routes = [ 9 | { 10 | path: "/", 11 | name: "Register", 12 | alias: ["/register", "/login"], 13 | component: LandingPage, 14 | meta: { 15 | title: () => { 16 | return "ADR Manager - Connect"; 17 | } 18 | } 19 | }, 20 | /** Route to the Editor (selects fitting sub-route) */ 21 | { 22 | path: "/manager", 23 | alias: ["/editor"], 24 | name: "Editor", 25 | component: EditorView, 26 | meta: { requiresAuth: true }, 27 | redirect: (to) => { 28 | if (to.params.organization && to.params.repo && to.params.branch && to.params.adr) { 29 | return { name: "EditorWithSpecifiedAdr" }; 30 | } else if (to.params.organization && to.params.repo && to.params.branch) { 31 | console.log("Route to spec repo"); 32 | return { name: "EditorWithSpecifiedRepo" }; 33 | } else { 34 | return { name: "EditorUnspecified" }; 35 | } 36 | } 37 | }, 38 | // Sub-route 1: If the route does not specify a repo 39 | { 40 | path: "/manager", 41 | name: "EditorUnspecified", 42 | component: EditorView, 43 | meta: { requiresAuth: true }, 44 | props: (route) => ({ ...route.query, ...route.params }) 45 | }, 46 | // Sub-route 2: If the route does not specify ADR but specifies a repo 47 | { 48 | path: "/manager/:organization/:repo/:branch", 49 | name: "EditorWithSpecifiedRepo", 50 | component: EditorView, 51 | meta: { requiresAuth: true }, 52 | props: (route) => ({ 53 | ...route.query, 54 | ...route.params, 55 | repoFullName: route.params.organization + "/" + route.params.repo, 56 | branch: route.params.branch 57 | }) 58 | }, 59 | // Sub-route 2: If the route does specify repo and ADR 60 | { 61 | path: "/manager/:organization/:repo/:branch/:adr", 62 | name: "EditorWithSpecifiedAdr", 63 | component: EditorView, 64 | meta: { 65 | requiresAuth: true, 66 | title: (route) => { 67 | return route.params.adr; 68 | } 69 | }, 70 | props: (route) => ({ 71 | ...route.query, 72 | ...route.params, 73 | repoFullName: route.params.organization + "/" + route.params.repo, 74 | branch: route.params.branch, 75 | adr: route.params.adr 76 | }) 77 | }, 78 | { 79 | path: "/*", 80 | name: "Error 404", 81 | component: ErrorPage 82 | } 83 | ]; 84 | 85 | const router = new VueRouter({ 86 | routes: routes 87 | }); 88 | 89 | router.beforeEach((to, from, next) => { 90 | if (to.matched.some((record) => record.meta.requiresAuth)) { 91 | // this route requires auth, check if logged in 92 | // if not, redirect to login page. 93 | if (!localStorage.getItem("authId")) { 94 | next({ 95 | path: "/login", 96 | query: { redirect: to.fullPath } 97 | }); 98 | } else { 99 | next(); 100 | } 101 | } else { 102 | next(); // make sure to always call next()! 103 | } 104 | }); 105 | 106 | const DEFAULT_TITLE = "ADR Manager"; 107 | router.afterEach((to) => { 108 | Vue.nextTick(() => { 109 | document.title = to.meta.title ? to.meta.title(to) : DEFAULT_TITLE; 110 | }); 111 | }); 112 | 113 | export default router; 114 | -------------------------------------------------------------------------------- /src/plugins/parser/MADR.g4: -------------------------------------------------------------------------------- 1 | // Define a grammar called Hello 2 | grammar MADR; 3 | 4 | start : 5 | HEADING_PREFIX title NEWLINE wslbs 6 | ( STATUS_MARKER status (WS OPTIONAL_MAKER)? wslbs )? 7 | ( DECIDERS_MARKER deciders (WS OPTIONAL_MAKER)? wslbs )? 8 | ( DATE_MARKER date (WS OPTIONAL_MAKER)? NEWLINE wslbs )? 9 | ( TECHNICAL_STORY_MARKER technicalStory (WS OPTIONAL_MAKER)? wslbs )? 10 | ( CONTEXT_AND_PROBLEM_STATEMENT wslbs )? 11 | ( NEWLINE contextAndProblemStatement wslbs )? // Text without a heading is interpreted as context and problem statement 12 | ( DECISION_DRIVERS_HEADING (WS OPTIONAL_MAKER)? wslbs decisionDrivers wslbs )? 13 | ( CONSIDERED_OPTIONS_HEADING wslbs consideredOptions wslbs )? 14 | ( DECISION_OUTCOME_HEADING wslbs decisionOutcome wslbs )? 15 | ( PROS_AND_CONS_OF_THE_OPTIONS_HEADING (WS OPTIONAL_MAKER)? wslbs prosAndConsOfOptions wslbs )? 16 | ( LINKS_HEADING (WS OPTIONAL_MAKER)? wslbs links wslbs )? 17 | EOF 18 | ; 19 | 20 | 21 | title : textLine ; 22 | 23 | status: textLine ; 24 | 25 | deciders : textLine ; 26 | 27 | date : textLine ; 28 | 29 | technicalStory : textLine ; 30 | 31 | contextAndProblemStatement : multilineText ; 32 | 33 | decisionDrivers : list ; 34 | 35 | consideredOptions : list ; 36 | 37 | decisionOutcome : wslbs chosenOptionAndExplanation 38 | (wslbs POSITIVE_CONSEQUENCES_HEADING (WS OPTIONAL_MAKER)? positiveConsequences)? 39 | (wslbs NEGATIVE_CONSEQUENCES_HEADING (WS OPTIONAL_MAKER)? negativeConsequences)? ; 40 | 41 | prosAndConsOfOptions : (optionSection wslbs)+ ; 42 | 43 | optionSection : 44 | SUBSUBHEADING_PREFIX optionTitle NEWLINE 45 | (wslbs optionDescription)? 46 | (wslbs prolist)? 47 | (wslbs conlist)? 48 | ; 49 | 50 | chosenOptionAndExplanation : multilineText ; 51 | positiveConsequences : list ; 52 | negativeConsequences : list ; 53 | 54 | optionTitle : textLine ; 55 | optionDescription : multilineText ; 56 | prolist : (wslbs LIST_MARKER 'Good, because ' textLine) + ; 57 | conlist : (wslbs LIST_MARKER 'Bad, because ' textLine) + ; 58 | 59 | links : list wslbs ; 60 | 61 | list : (wslbs LIST_MARKER textLine?) + ; 62 | 63 | textLine : (any | WS) +? ; 64 | 65 | multilineText : (any | wslb) +? ; // Any (possibly multi-line) text 66 | 67 | any : ( WORD | CHARACTER | LIST_MARKER | HEADING_PREFIX | SUBSUBSUBHEADING_PREFIX) ; 68 | 69 | wslb : ( WS | NEWLINE ) ; 70 | wslbs : wslb *; 71 | 72 | /// Tokenization / Lexer rules 73 | 74 | WORD : CHARACTER+; 75 | CHARACTER : (~[\n\t\r\f ] ) ; 76 | 77 | WS : [\f\t ] ; // White Space 78 | NEWLINE : [\r]?[\n] ; // Line Breaks 79 | 80 | LIST_MARKER : NEWLINE ('* ' | '- '); 81 | STATUS_MARKER : LIST_MARKER 'Status: '; 82 | DATE_MARKER : LIST_MARKER 'Date: '; 83 | DECIDERS_MARKER : LIST_MARKER 'Deciders: '; 84 | OPTIONAL_MAKER: ''; 85 | TECHNICAL_STORY_MARKER : NEWLINE 'Technical Story: '; 86 | 87 | HEADING_PREFIX : '# ' ; // Start of a Heading 88 | SUBSUBHEADING_PREFIX : NEWLINE '### ' ; // Start of a Sub sub heading (e. g. an option section) 89 | SUBSUBSUBHEADING_PREFIX : '###' '#'+ ' ' ; // Start of a sub sub sub heading (no special meaning, should be accepted in multiline text) 90 | 91 | // Headings 92 | CONTEXT_AND_PROBLEM_STATEMENT : NEWLINE ( '## Context and Problem Statement' | '## Context and problem statement' ); 93 | DECISION_DRIVERS_HEADING : NEWLINE ( '## Decision Drivers' | '## Decision drivers' ) ; 94 | CONSIDERED_OPTIONS_HEADING : NEWLINE ( '## Considered Options' | '## Considered options' ) ; 95 | DECISION_OUTCOME_HEADING : NEWLINE ( '## Decision Outcome' | '## Decision outcome' ) ; 96 | POSITIVE_CONSEQUENCES_HEADING : NEWLINE ( '### Positive Consequences' | '### Positive consequences') ; 97 | NEGATIVE_CONSEQUENCES_HEADING : NEWLINE ( '### Negative Consequences' | '### Negative consequences') ; 98 | PROS_AND_CONS_OF_THE_OPTIONS_HEADING : NEWLINE ( '## Pros and Cons of the Options' | '## Pros and cons of the options') ; 99 | LINKS_HEADING : NEWLINE '## Links' ; 100 | -------------------------------------------------------------------------------- /docs/evaluation/user-study/results/case-descriptions/case-description-07.md: -------------------------------------------------------------------------------- 1 | # Case Description 7 2 | 3 | ## Demographic and Professional Background 4 | 5 | * Role: enterprise architect 6 | * Domain: software and IT services 7 | * Years of work experience: 20 years 8 | * ADR experience: 10 years 9 | * Team size: 40 people 10 | * Used template: different templates that depend on project, no experience with [MADR](https://github.com/adr/madr) 11 | * Tools: Confluence 12 | 13 | ## Think-Aloud Protocol 14 | 15 | _Abbreviations_: In the following, each statement is started with a letter describing the context of the statement. 16 | * O = Observation: An observation by the researchers that the participant did not explicitly mention. 17 | * P = Participant: A statement of the participant. 18 | * I = Interference: The researchers helped or pointed out something that the participant had trouble with. 19 | 20 | Needed time: 10 min 21 | 22 | * Technical problem while connecting to GitHub: very long loading time and repositories did not load (possibly the Internet connection). 23 | * After disconnecting and connecting again, there were no problems anymore. 24 | * P: Participant gets confused by default text of the `because` field of `Decision Outcome`. 25 | * I: Organizers inform about different modes. Participant switches to professional mode. 26 | * O: Participant tries to enter several advantages and disadvantages in one field at the decision outcome. 27 | * O: Deciders are not directly clear. 28 | * O: The title, the date, and the status are filled in at the end. 29 | * O: The participant does not find the possibility to expand considered options. 30 | * O: The `Commit and Push` button was not found directly. P: The `Commit and Push` button could be highlighted more. 31 | * O: Push dialog was understandable. 32 | 33 | Notes: 34 | 35 | * Bug when putting a `/` in the title field (Has already been fixed) 36 | 37 | ## Interview 38 | 39 | ### Functional Suitability 40 | 41 | **Which provided functionality needs to be improved?** 42 | 43 | * All functions worked well and do not need to be improved. Only UI improvements and additional functionalities were mentioned. 44 | 45 | **Which additional functionality would you need to consider using the tool in your professional work?** 46 | 47 | * Field for vote. 48 | * Including diagrams or images, e.g. of a whiteboard. 49 | * Resubmission date. 50 | * Include opinions of deciders and who was responsible. 51 | 52 | ### Usability 53 | 54 | **How understandable was the user interface for you?** 55 | 56 | * Did not see modes at all in the beginning. 57 | * Possible solution: wizard for selecting a mode, when opening the tool and creating the ADR. 58 | * Save button should be bottom right or green and bigger. 59 | * Workflow was intuitive. 60 | 61 | **How efficiently usable was the user interface for you?** 62 | 63 | * Good except for a few small starting problems that were clear after the first use (modes, expandable options, push button). 64 | 65 | **Which mode would you use most often?** 66 | 67 | * Basic mode to record the info shortly after the meeting. Professional to elaborate everything. 68 | 69 | #### Would you switch between modes? 70 | 71 | * Yes. 72 | 73 | ### Final Verdict 74 | 75 | | Statement | strongly disagree | disagree | neutral | agree | strongly agree | 76 | | --------------------------------------------------------------------- | :---------------: | :------: | :-----: | :---: | :------------: | 77 | | 1. The functional suitability of the tool is fitting for its purpose. | | | x | | | 78 | | 2. The usability of the tool allows its convenient usage. | | | x | | | 79 | | 3. It is likely that I will use the tool in the future. | | | | x | | 80 | 81 | Additional comments: 82 | 83 | - Considered usage only if it will support BitBucket. 84 | -------------------------------------------------------------------------------- /docs/evaluation/user-study/results/case-descriptions/case-description-05.md: -------------------------------------------------------------------------------- 1 | # Case Description 5 2 | 3 | ## Demographic and Professional Background 4 | 5 | * Role: lead architect and developer 6 | * Domain: quantum computing and AI 7 | * Years of work experience: 4 years 8 | * ADR experience: 6 years 9 | * Team size: 4-10 people 10 | * Used template: [MADR](https://github.com/adr/madr) 11 | * Tools: None specifically, mostly GitHub Editor 12 | 13 | ## Think-Aloud Protocol 14 | 15 | _Abbreviations_: In the following, each statement is started with a letter describing the context of the statement. 16 | * O = Observation: An observation by the researchers that the participant did not explicitly mention. 17 | * P = Participant: A statement of the participant. 18 | * I = Interference: The researchers helped or pointed out something that the participant had trouble with. 19 | 20 | Needed time: 18 min 21 | 22 | * There were technical problems when using the browser Safari, so the participant uses Chrome instead. 23 | * O: No problems while connecting to GitHub, adding a repository, and creating a new ADR. 24 | * O: Participant tries to fill in full descriptions, pros and cons of the options as Markdown in Basic mode (where the UI only expects option titles). 25 | * O: After switching to `Raw Markdown`-Tab and back the content is lost. 26 | * I: Organizers inform them about different modes. 27 | * O: Participant switches to Professional mode. 28 | * O: Participant understands UI concept of options (only) after they filled in a new option title in `Professional` mode and it automatically expands. 29 | * P: Participant would like "automatic" lists. 30 | * P: Icon of deciders confused participant. 31 | * They expected it to behave like the lists, i.e. that a new text field appears for additional deciders. 32 | 33 | ## Interview 34 | 35 | ### Functional Suitability 36 | 37 | **Which provided functionality needs to be improved?** 38 | 39 | * They liked that the interface was very simple and did not have too many functions. 40 | 41 | **Which additional functionality would you need to consider using the tool in your professional work?** 42 | 43 | * Support for referencing an ADR in a commit and showing the related commits when displaying the ADR. 44 | * Support for superseding / deprecation of ADRs and navigating from one ADR to another. 45 | * Partially satisfied by `Status` field, but misses possibility to reference other ADRs. 46 | 47 | ### Usability 48 | 49 | **How understandable was the user interface for you?** 50 | 51 | * Consistency: When there appears a new text field for lists, then it should do so for `Deciders`, too. 52 | * The modes are nice because they allow to start with quickly sketching an ADR and then writing more details. However, a workflow to use them naturally is missing. 53 | * Mini-HowTo for modes would help. 54 | 55 | **How efficiently usable was the user interface for you?** 56 | 57 | * Very efficient 58 | * It's nice that the user does not have to take care of the Markdown. 59 | 60 | **Which mode would you use most often?** 61 | 62 | * Basic mode for quick drafting and Professional for more detailed ADRs. 63 | * They most likely would not need Advanced mode. 64 | 65 | #### Would you switch between modes? 66 | 67 | * Most likely yes 68 | 69 | ### Final Verdict 70 | 71 | | Statement | strongly disagree | disagree | neutral | agree | strongly agree | 72 | | --------------------------------------------------------------------- | :---------------: | :------: | :-----: | :---: | :------------: | 73 | | 1. The functional suitability of the tool is fitting for its purpose. | | | | x | | 74 | | 2. The usability of the tool allows its convenient usage. | | | | x | | 75 | | 3. It is likely that I will use the tool in the future. | | | | | x | 76 | 77 | Additional comments: 78 | 79 | - Missing deprecation of ADRs. 80 | - Tab key for navigating between text fields would be nice. 81 | - Only regular usage if the tool will support GitLab. 82 | -------------------------------------------------------------------------------- /cypress/e2e/adrManagerTest/PushNewAdr.cy.js: -------------------------------------------------------------------------------- 1 | import { REST_BRANCH_URL, REST_LIST_REPO_URL, REST_COMMIT_URL, TEST_BASE_URL } from "../../support/e2e"; 2 | 3 | context("Committing, pushing, and remote-deleting an ADR", () => { 4 | it("Commit and push new ADR, then delete from GitHub", () => { 5 | const REPO_NAME = "adr/adr-test-repository-empty"; 6 | const BRANCH_NAME = "testing-branch"; 7 | 8 | function addRepositoryAndSwitchBranch() { 9 | cy.intercept("GET", REST_LIST_REPO_URL).as("getRepos"); 10 | cy.get("[data-cy=addRepo]").click(); 11 | cy.wait("@getRepos").its("response.statusCode").should("eq", 200); 12 | cy.get("[data-cy=search-field-for-adding-repository]").type(REPO_NAME); 13 | cy.wait("@getRepos").its("response.statusCode").should("eq", 200); 14 | cy.get("[data-cy=listRepo]").contains(REPO_NAME).click(); 15 | cy.get("[data-cy=addRepoDialog]").click(); 16 | cy.get("[data-cy=repoNameList]").click(); 17 | 18 | // Select branch 19 | // Trigger loading of the branch 20 | cy.get("[data-cy=branchSelect]").trigger("click"); 21 | // Select branch to commit to 22 | cy.get("[data-cy=branchSelect]").select(BRANCH_NAME); 23 | // Accept 24 | cy.contains("OK").click(); 25 | 26 | // Make sure the branch was changed. 27 | cy.get("[data-cy=branchSelect]").contains(BRANCH_NAME); 28 | 29 | // Reloading the repository typically takes some time ... 30 | cy.wait(2000); 31 | } 32 | window.localStorage.clear(); 33 | window.localStorage.setItem("authId", Cypress.env("OAUTH_E2E_AUTH_ID")); 34 | window.localStorage.setItem("user", Cypress.env("USER")); 35 | cy.visit(TEST_BASE_URL); 36 | addRepositoryAndSwitchBranch(); 37 | 38 | // add new ADR 39 | cy.get("[data-cy=NewAdrFile]").click({ force: true }); 40 | cy.get("[data-cy=titleAdr]").type("use x to accomplish y"); 41 | // initiate commit dialog 42 | cy.get("[data-cy=pushIcon]").click(); 43 | // check for dialog 44 | cy.get("[data-cy=btnOfDialogCommitForPush]").should("be.disabled"); 45 | cy.get("[data-cy=mdiAlertNotSelected]").should("be.visible"); 46 | cy.get("[data-cy=mdiAlertCommitMessage]").should("be.visible"); 47 | // set commit message and commit 48 | cy.get("[data-cy=newFilesCommitMessage]").click(); 49 | cy.get("[data-cy=newFileCheckBoxOuter]").contains(/[0-9][0-9][0-9][0-9]-use-x-to-accomplish-y.md/g); 50 | cy.get("[data-cy=newFileCheckBox]").check({ force: true }); 51 | cy.get("[data-cy=mdiCheckSelected]").should("be.visible"); 52 | cy.get("[data-cy=textFieldCommitMessage]").type("[E2ETest] Add a new ADR"); 53 | cy.get("[data-cy=mdiCheckCommitMessage]").should("be.visible"); 54 | // push to GitHub 55 | cy.get("[data-cy=btnOfDialogCommitForPush]").click(); 56 | 57 | cy.intercept("GET", REST_BRANCH_URL).as("getCommitSha"); 58 | cy.intercept("POST", REST_COMMIT_URL).as("commitRequest"); 59 | cy.contains("OK").click(); 60 | 61 | // Remove repository 62 | cy.get("[data-cy=removeRepo]").click(); 63 | cy.get("[data-cy=removeRepoBtn]").click(); 64 | cy.get("[data-cy=listRepo]").should("have.length", 0); 65 | 66 | // Re-add the repository 67 | addRepositoryAndSwitchBranch(); 68 | 69 | // Check that element was added 70 | cy.get("[data-cy=adrList]").filter(':contains("Use X To Accomplish Y")').should("have.length.gte", 1); 71 | 72 | // delete the ADR 73 | cy.get("[data-cy=adrList]") 74 | .filter(':contains("Use X To Accomplish Y")') 75 | .find("[data-cy=deleteAdrBtn]") 76 | .each((el) => { 77 | el.click(); 78 | cy.get("[data-cy=dialogDeleteAdrBtn] :visible").first().click(); 79 | }); 80 | 81 | // commit and push 82 | cy.wait(5000); 83 | cy.get("[data-cy=pushIcon]").click(); 84 | cy.get("[data-cy=deletedFilesAdr]").click(); 85 | cy.get("[data-cy=deletedFileCheckBox]").check({ force: true }); 86 | cy.get("[data-cy=textFieldCommitMessage]").type("[E2ETest] Delete the ADR"); 87 | cy.get("[data-cy=btnOfDialogCommitForPush]").click(); 88 | cy.contains("OK").click(); 89 | cy.wait("@getCommitSha"); 90 | }); 91 | }); 92 | -------------------------------------------------------------------------------- /docs/evaluation/user-study/results/case-descriptions/case-description-08.md: -------------------------------------------------------------------------------- 1 | # Case Description 8 2 | 3 | ## Demographic and Professional Background 4 | 5 | * Role: architect and developer 6 | * Domain: backend, data science 7 | * Years of work experience: 20 years 8 | * ADR experience: 10 years 9 | * Team size: 5-15 person 10 | * Used template: specific for each project 11 | * Tools: Wikiwebs, Sharepoint, Git with Markdown 12 | 13 | ## Think-Aloud Protocol 14 | 15 | _Abbreviations_: In the following, each statement is started with a letter describing the context of the statement. 16 | * O = Observation: An observation by the researchers that the participant did not explicitly mention. 17 | * P = Participant: A statement of the participant. 18 | * I = Interference: The researchers helped or pointed out something that the participant had trouble with. 19 | 20 | Needed time: 20 min 21 | 22 | * There were technical problems while connecting to GitHub. This was due to a `no script` plugin, as Heroku requires JavaScript. 23 | * P: Confusion when adding a repository. Where should the repository be taken from? Local? 24 | * O: Participant creates an ADR. 25 | * P: Spelling correction would be very helpful. 26 | * O: Participant fills in the fields in `Considered Options`. 27 | * O: Participant fills in the fields in `Decision Outcome`. 28 | * I: Organizers inform about modes because the participant believes they are done with filling in the ADR. 29 | * O: Participant fills in the fields in `Positive Consequences`. 30 | * O: Participant fills in the fields in `Negative Consequences`. 31 | * O: Participant fills in the fields in `Decision Drivers`. 32 | * O: Participant switches to `Raw Markdown`. 33 | * I: With the help of the researchers, the participant can find the additional fields in `Considered Options`. 34 | * O: Participant fills in the fields in `Good, because...` and `Bad, because...`. 35 | * O: Participant commits and pushes the ADR. 36 | 37 | ## Interview 38 | 39 | ### Functional Suitability 40 | 41 | **Which provided functionality needs to be improved?** 42 | 43 | * Needed functionality is there. 44 | * Additional free text field (if it doesn't break concept of ADRs). 45 | * `Convert` should give clearer information that content is lost. 46 | * Raw Markdown mode seems like you can write whatever you want but it is lost when switching to Editor, which is very frustrating. 47 | 48 | **Which additional functionality would you need to consider using the tool in your professional work?** 49 | 50 | * Include or link images in Markdown. 51 | * Reset content of a single file and repository. 52 | * Option to determine who edited the ADR and when. List of people who have said what. 53 | * Undo function 54 | * Function to see what has been changed and the changes between different versions. Show Git history of the ADRs. 55 | 56 | ### Usability 57 | 58 | **How understandable was the user interface for you?** 59 | 60 | * Sufficiently well 61 | * For new users, more explanations in the UI would be helpful, e.g. through explanations on hover 62 | * Modes should be explained. 63 | * Deletion of options should give a warning. 64 | 65 | **How efficiently usable was the user interface for you?** 66 | 67 | * Not really efficient, the participant will prefer to write raw Markdown with Emacs. 68 | 69 | **Which mode would you use most often?** 70 | 71 | * `Professional` mode, because it is simple enough. 72 | 73 | #### Would you switch between modes? 74 | 75 | * No, the `Professional` mode is enough. 76 | 77 | ### Final Verdict 78 | 79 | | Statement | strongly disagree | disagree | neutral | agree | strongly agree | 80 | | --------------------------------------------------------------------- | :---------------: | :------: | :-----: | :---: | :------------: | 81 | | 1. The functional suitability of the tool is fitting for its purpose. | | | | x | | 82 | | 2. The usability of the tool allows its convenient usage. | | | | x | | 83 | | 3. It is likely that I will use the tool in the future. | x | | | | | 84 | 85 | Additional comments: 86 | 87 | - Functional completeness (4), functional correctness (4), functional appropriateness (4) 88 | - Learnability (4), operability (4), aesthetics (3) 89 | - Templates are dynamic and the used tools depend on the team but, in general, the tool could be used in this field. 90 | -------------------------------------------------------------------------------- /docs/evaluation/user-study/results/case-descriptions/case-description-01.md: -------------------------------------------------------------------------------- 1 | # Case Description 1 2 | 3 | ## Demographic and Professional Background 4 | 5 | * Role: Professor, software architecture lecturer 6 | * Domain: software quality and architecture, some experience in other fields (business information systems, autonomous systems) 7 | * Years of work experience: 20 years 8 | * ADR experience: 10 years, but not with [MADR](https://github.com/adr/madr) 9 | * Team size: _(question was not asked)_ 10 | * Used template: none, free text in documentation 11 | * Tools: depends on project, e.g. Jira or other documentation like a READMEs 12 | 13 | ## Think-Aloud Protocol 14 | 15 | _Abbreviations_: In the following, each statement is started with a letter describing the context of the statement. 16 | * O = Observation: An observation by the researchers that the participant did not explicitly mention. 17 | * P = Participant: A statement of the participant. 18 | * I = Interference: The researchers helped or pointed out something that the participant had trouble with. 19 | 20 | Needed time: 18 min 21 | 22 | * O: No problems while connecting to GitHub, adding a repository, and creating an ADR. 23 | * P: Title field is confusing, as the field does not have a label. 24 | * O: Participant switches to Professional mode. 25 | * P: In general, there should be more labels and explanations for the fields. 26 | * O: Participant fills in `Decision Drivers`. 27 | * O: While filling in the `Considered Options`, participant does not see how to add the second option. 28 | * Note: The problem may be that the first option expanded automatically and thus there were many fields. After collapsing the option, the participant understood that the last field is for the next option. 29 | * O: Participant fills in the fields in `Decision Outcome`. 30 | * O: Participant notices that branch selection is bugged. 31 | * I: Organizers show where the `Commit and Push` button is, because the participant does not find it. 32 | * P: `Commit and Push` icon is confusing. 33 | 34 | ## Interview 35 | 36 | ### Functional Suitability 37 | 38 | **Which provided functionality needs to be improved?** 39 | 40 | * Connection to GitHub was easy. 41 | * Needed functionality is there. 42 | 43 | **Which additional functionality would you need to consider using the tool in your professional work?** 44 | 45 | * A plugin in the IDE would be better, as it wouldn't require a context switch and it would fit better to the participant's workflow. 46 | * Using free text with syntax highlighting and feedback for completeness would be better. 47 | * (Template) feature suggestion: Weighting decision drivers and pros/cons with stars to see which ones are more important. 48 | 49 | ### Usability 50 | 51 | **How understandable was the user interface for you?** 52 | 53 | * UI is confusing as there are too few labels. Possible improvements: 54 | * Labels for all test fields 55 | * Explanations, e.g. through explanations on hover 56 | * Maybe wizard for guidance 57 | * Good: interactive and modern 58 | 59 | **How efficiently usable was the user interface for you?** 60 | 61 | * Raw Markdown may be faster. 62 | * IDE plugin would be better. 63 | * The web application may be prettier in meetings. 64 | 65 | **Which mode would you use most often?** 66 | 67 | * Depends on the decision to be documented 68 | * Smaller decisions can be documented in Basic mode. 69 | * Most architectural decisions are complex enough that Professional mode is required. Particularly, the rationale behind the decision, e.g. decision drivers, are important to capture. 70 | 71 | #### Would you switch between modes? 72 | 73 | * Maybe 74 | 75 | ### Final Verdict 76 | 77 | | Statement | strongly disagree | disagree | neutral | agree | strongly agree | 78 | | --------------------------------------------------------------------- | :---------------: | :------: | :-----: | :---: | :------------: | 79 | | 1. The functional suitability of the tool is fitting for its purpose. | | | | x | | 80 | | 2. The usability of the tool allows its convenient usage. | | | x | | | 81 | | 3. It is likely that I will use the tool in the future. | | x | | | | 82 | 83 | Additional comments: 84 | 85 | - Usability facets: learnability (1), aesthetics (4 or 5) 86 | - They would use the tool only if it was a plugin for Visual Studio Code. They would maybe use the tool in meetings. 87 | -------------------------------------------------------------------------------- /docs/evaluation/user-study/results/case-descriptions/case-description-02.md: -------------------------------------------------------------------------------- 1 | # Case Description 2 2 | 3 | ## Demographic and Professional Background 4 | 5 | * Role: software architecture lecturer, consultant 6 | * Domain: integration architectures in general, no specific domain 7 | * Years of work experience: 32 years work experience, 15 years as researcher, 7 years as lecturer 8 | * ADR experience: 20 years 9 | * Team size: varies a lot, both alone and in large teams 10 | * Used template: Y-Statements 11 | * [MADR](https://github.com/adr/madr) only in toy examples 12 | * Tools: plain text in Markdown, sometimes [embedded ADRs](https://github.com/adr/e-adr) when in combination with code 13 | 14 | ## Think-Aloud Protocol 15 | 16 | _Abbreviations_: In the following, each statement is started with a letter describing the context of the statement. 17 | * O = Observation: An observation by the researchers that the participant did not explicitly mention. 18 | * P = Participant: A statement of the participant. 19 | * I = Interference: The researchers helped or pointed out something that the participant had trouble with. 20 | 21 | Needed time: 18 min 22 | 23 | * O: No problems while connecting to GitHub and adding the repository. 24 | * P: Since the `New ADR` button is so big, it could be labeled `new architectural decision record`. 25 | * O: Participant copy-pastes option titles and understands that each option needs to be put in a different line. 26 | * P: Participant is unsure if they should save the ADR or whether it is auto-saved. 27 | * I: Organizers inform about modes. 28 | * P: `Basic, Advanced, Professional` should have a small explanation, maybe label it with `Usage Mode: `. 29 | * P: Participant copy-pastes consequences (i.e. list in one field). 30 | * O: After going to the Preview tab and back, they land in Convert-Tab. This tab was confusing. Participant did not immediately understand what to do there. Particularly, they did not know what is meant by "MADR-Editor". 31 | * P: Difference between `Advanced` and `Professional` mode seems too small. 32 | * P: Participant didn't find that they could expand options in Professional mode on their own, but they liked the concept of modes. 33 | * Expanding the option when clicking on pencil would be more intuitive. 34 | * O: No problems while pushing to GitHub. 35 | * P: The `Commit and Push` icon may be confusing. 36 | 37 | ## Interview 38 | 39 | ### Functional Suitability 40 | 41 | **Which provided functionality needs to be improved?** 42 | 43 | * Good: every part of Y-Statements exists in MADR. 44 | * It should also be possible to document changes over time. 45 | 46 | **Which additional functionality would you need to consider using the tool in your professional work?** 47 | 48 | * Sometimes, it's not XOR for the options 49 | * Support for changes over time: sometimes, one option is chosen as a fast solution but another option is planned to be implemented later 50 | * Combinations of options 51 | * Offline mode (often, documentation is done offline without Internet) 52 | * Import of existing ADRs 53 | * Automatic generation of a concise report as pretty presentation, e.g. Y-Statements can be put on one slide. 54 | * e.g. using PowerPoint or a white board like Miro 55 | 56 | ### Usability 57 | 58 | **How understandable was the user interface for you?** 59 | 60 | * Very well understandable, not too many hints and not too few 61 | 62 | **How efficiently usable was the user interface for you?** 63 | 64 | * Good in the example of this study 65 | * Copy-paste (particularly in lists) may be annoying over time 66 | * Visual Studio Code plugin may be better 67 | 68 | **Which mode would you use most often?** 69 | 70 | * Basic and Professional 71 | 72 | #### Would you switch between modes? 73 | 74 | * Yes. 75 | 76 | ### Final Verdict 77 | 78 | | Statement | strongly disagree | disagree | neutral | agree | strongly agree | 79 | | --------------------------------------------------------------------- | :---------------: | :------: | :-----: | :---: | :------------: | 80 | | 1. The functional suitability of the tool is fitting for its purpose. | | | | x | | 81 | | 2. The usability of the tool allows its convenient usage. | | | | | x | 82 | | 3. It is likely that I will use the tool in the future. | | | | | x | 83 | 84 | Additional comments: 85 | 86 | - Participant is likely to try out the tool, but unsure whether usage will be long-term. 87 | -------------------------------------------------------------------------------- /docs/evaluation/user-study/results/case-descriptions/case-description-03.md: -------------------------------------------------------------------------------- 1 | # Case Description 3 2 | 3 | ## Demographic and Professional Background 4 | 5 | * Role: PhD Student, currently sole developer: lead developer, architect, and other roles 6 | * Domain: research, generation of unit test cases, open source 7 | * Years of work experience: 3 years 8 | * ADR experience: 2 years 9 | * Team size: 2-5 people 10 | * Used template: formless, sometimes with diagrams 11 | * Tools: pen and paper or text documents in Git 12 | 13 | ## Think-Aloud Protocol 14 | 15 | _Abbreviations_: In the following, each statement is started with a letter describing the context of the statement. 16 | * O = Observation: An observation by the researchers that the participant did not explicitly mention. 17 | * P = Participant: A statement of the participant. 18 | * I = Interference: The researchers helped or pointed out something that the participant had trouble with. 19 | 20 | Needed time: 13 min 21 | 22 | * O: No problems while connecting to GitHub, adding a repository, and creating an ADR. 23 | * O: No problems while filling in title, options, and selecting the chosen option. 24 | * P: The default text `(see below)` of the `because` field in `Decision Outcome` is confusing. 25 | * I: Organizers inform participant about modes. 26 | * P: Participant is unsure what the fields `Deciders`, `Technical Story`, and `Decision Drivers` are for. Particularly: 27 | * Should the `Deciders` field contain all deciders or just the recorder of the meeting? 28 | * What is the difference between `Technical Story` and `Context and Problem Statement`? 29 | * O: Participant finds possibility to expand options (by accident). 30 | * P: Preview of the formatted Markdown is good. 31 | * O: No problems while pushing to GitHub. 32 | 33 | ## Interview 34 | 35 | ### Functional Suitability 36 | 37 | **Which provided functionality needs to be improved?** 38 | 39 | * Given functions of the tool work well. 40 | * The text field for deciders was confusing. It should be like lists. 41 | 42 | **Which additional functionality would you need to consider using the tool in your professional work?** 43 | 44 | * Support for including images. 45 | * GitHub as host is great for the participant but their colleagues would require GitLab. 46 | * If the participant wanted to capture ADRs in teams, they would use the tool, because it gives an external structure. 47 | 48 | ### Usability 49 | 50 | **How understandable was the user interface for you?** 51 | 52 | * Modes were confusing. It was hard to understand that they were related to the editor. 53 | * More explanations for each sections would be helpful, e.g. a question mark next to title which shows an explanation on hover. 54 | * List items should have a dot at the start to show that it's a list. 55 | * Multi-line text fields (like `Context and Problem Statement`) should be bigger. 56 | * In the `Convert` tab, it's confusing what to do when one does not want to accept. Maybe a button saying `Cancel` or `Let me edit it` could be added that redirects to `Raw Markdown` tab. 57 | * "Would a short tutorial at the start be helpful?" --> A tutorial may overdo it, but a short motivational text which explains what ADRs are for would be helpful. 58 | 59 | **How efficiently usable was the user interface for you?** 60 | 61 | * Good, the UI is fast and clearly structured. 62 | 63 | **Which mode would you use most often?** 64 | 65 | * The participant would prefer to customize which fields are visible, e.g. they would use `Links` (from Professional mode) but not `Technical Story` (from Advanced mode). Ideally, all headings should be visible but collapsible. 66 | 67 | #### Would you switch between modes? 68 | 69 | * Maybe 70 | 71 | ### Final Verdict 72 | 73 | | Statement | strongly disagree | disagree | neutral | agree | strongly agree | 74 | | --------------------------------------------------------------------- | :---------------: | :------: | :-----: | :---: | :------------: | 75 | | 1. The functional suitability of the tool is fitting for its purpose. | | | | | x | 76 | | 2. The usability of the tool allows its convenient usage. | | | | x | | 77 | | 3. It is likely that I will use the tool in the future. | | | x | | | 78 | 79 | Additional comments: 80 | 81 | - Functional completeness (5), functional correctness (5), functional appropriateness (4) 82 | - Learnability (3), operability (5), user interface aesthetics (4) 83 | - Very dependent on the other members of a team 84 | -------------------------------------------------------------------------------- /src/components/EditorConvert.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 131 | 132 | 140 | -------------------------------------------------------------------------------- /docs/evaluation/user-study/results/case-descriptions/case-description-04.md: -------------------------------------------------------------------------------- 1 | # Case Description 4 2 | 3 | ## Demographic and Professional Background 4 | 5 | * Role: principal architect, data engineer 6 | * Domain: consulting 7 | * Years of work experience: 10 8 | * ADR experience: 5 years 9 | * Team size: 5-8 people 10 | * Used template: none, project-specific 11 | * Tools: none specifically 12 | 13 | ## Think-Aloud Protocol 14 | 15 | _Abbreviations_: In the following, each statement is started with a letter describing the context of the statement. 16 | * O = Observation: An observation by the researchers that the participant did not explicitly mention. 17 | * P = Participant: A statement of the participant. 18 | * I = Interference: The researchers helped or pointed out something that the participant had trouble with. 19 | 20 | Needed time: 14 min 21 | 22 | * O: No problems while connecting to GitHub, adding a repository, and creating an ADR. 23 | * O: Participant overlooks the field for the title and fills in other information first. 24 | * O: Participant first puts all options in one option field. Then gets confused when the `Chosen option` dropdown only shows it as one option. 25 | * I: Organizers inform about different fields in considered options list. 26 | * O: Participant gets confused by default text of the `because` field of `Decision Outcome`. 27 | * I: Organizers inform about different modes. Participant switches to advanced mode. 28 | * O: Participant gets confused by the name `0000-.md` of the file in the `Commit and Push` Dialog, then notices the title field of the MADR and fills it in. 29 | * I: Organizers inform about expandability of considered options, calendar function, status, and deciders in Advanced mode. 30 | * O: Icon of deciders confuses participant. 31 | * They expected it to behave like the lists, i.e. that a new text field appears for additional deciders. 32 | * O: There were no problems while committing and pushing (upload sign for push is clear). 33 | 34 | ## Interview 35 | 36 | ### Functional Suitability 37 | 38 | **Which provided functionality needs to be improved?** 39 | 40 | * Modes were confusing: when to use each mode? 41 | * It seems like you can always use Professional mode. 42 | * Does not see the advantages of the different modes. 43 | * Having fields that are only visible in one mode was confusing. 44 | * Support for copy-paste in lists. 45 | * As a professional tool, it would need auto completion for decision makers. 46 | 47 | **Which additional functionality would you need to consider using the tool in your professional work?** 48 | 49 | * Search for decisions as well as within decisions. 50 | * Linking decisions to other decisions, e.g. one decision often depends on others or is followed by others. 51 | * Including images 52 | * Either by pushing an image file to GitHub and linking it in Markdown or using rich text to create diagrams. 53 | 54 | ### Usability 55 | 56 | **How understandable was the user interface for you?** 57 | 58 | * User interface is easy to understand. 59 | * Having the `Commit and Push` button directly next to the `Remove repository` button may be dangerous. 60 | * Modes were confusing at first, but once you know the differences they are understandable. 61 | * The `Title` field was confusing at first. It should have a label like the other fields. 62 | 63 | **How efficiently usable was the user interface for you?** 64 | 65 | * If you know the workflow, the interface is good to use. 66 | 67 | **Which mode would you use most often?** 68 | 69 | * Professional mode 70 | 71 | #### Would you switch between modes? 72 | 73 | * Currently, participant would use basic mode for quick drafts, because the options expand automatically in advanced and professional mode. 74 | * However, with a few changes, the participant would only use the professional mode: 75 | * Default expansion of new option items slows down writing progress. If the considered options did not expand automatically in professional mode (description and pros and cons), there would be no need for basic mode. 76 | * The advanced mode would not be needed, since the links and the decision drivers do not take up so much space. 77 | 78 | ### Final Verdict 79 | 80 | | Statement | strongly disagree | disagree | neutral | agree | strongly agree | 81 | | --------------------------------------------------------------------- | :---------------: | :------: | :-----: | :---: | :------------: | 82 | | 1. The functional suitability of the tool is fitting for its purpose. | | | | x | | 83 | | 2. The usability of the tool allows its convenient usage. | | | | x | | 84 | | 3. It is likely that I will use the tool in the future. | | | x | | | 85 | 86 | Additional comments: 87 | 88 | - Strongly depends on client. As consultant, the participant may propose the tool to the client. 89 | -------------------------------------------------------------------------------- /src/plugins/parser/MADRListener.js: -------------------------------------------------------------------------------- 1 | // Generated from MADR.g4 by ANTLR 4.13.0 2 | // jshint ignore: start 3 | import antlr4 from "antlr4"; 4 | 5 | // This class defines a complete listener for a parse tree produced by MADRParser. 6 | export default class MADRListener extends antlr4.tree.ParseTreeListener { 7 | // Enter a parse tree produced by MADRParser#start. 8 | enterStart(ctx) {} 9 | 10 | // Exit a parse tree produced by MADRParser#start. 11 | exitStart(ctx) {} 12 | 13 | // Enter a parse tree produced by MADRParser#title. 14 | enterTitle(ctx) {} 15 | 16 | // Exit a parse tree produced by MADRParser#title. 17 | exitTitle(ctx) {} 18 | 19 | // Enter a parse tree produced by MADRParser#status. 20 | enterStatus(ctx) {} 21 | 22 | // Exit a parse tree produced by MADRParser#status. 23 | exitStatus(ctx) {} 24 | 25 | // Enter a parse tree produced by MADRParser#deciders. 26 | enterDeciders(ctx) {} 27 | 28 | // Exit a parse tree produced by MADRParser#deciders. 29 | exitDeciders(ctx) {} 30 | 31 | // Enter a parse tree produced by MADRParser#date. 32 | enterDate(ctx) {} 33 | 34 | // Exit a parse tree produced by MADRParser#date. 35 | exitDate(ctx) {} 36 | 37 | // Enter a parse tree produced by MADRParser#technicalStory. 38 | enterTechnicalStory(ctx) {} 39 | 40 | // Exit a parse tree produced by MADRParser#technicalStory. 41 | exitTechnicalStory(ctx) {} 42 | 43 | // Enter a parse tree produced by MADRParser#contextAndProblemStatement. 44 | enterContextAndProblemStatement(ctx) {} 45 | 46 | // Exit a parse tree produced by MADRParser#contextAndProblemStatement. 47 | exitContextAndProblemStatement(ctx) {} 48 | 49 | // Enter a parse tree produced by MADRParser#decisionDrivers. 50 | enterDecisionDrivers(ctx) {} 51 | 52 | // Exit a parse tree produced by MADRParser#decisionDrivers. 53 | exitDecisionDrivers(ctx) {} 54 | 55 | // Enter a parse tree produced by MADRParser#consideredOptions. 56 | enterConsideredOptions(ctx) {} 57 | 58 | // Exit a parse tree produced by MADRParser#consideredOptions. 59 | exitConsideredOptions(ctx) {} 60 | 61 | // Enter a parse tree produced by MADRParser#decisionOutcome. 62 | enterDecisionOutcome(ctx) {} 63 | 64 | // Exit a parse tree produced by MADRParser#decisionOutcome. 65 | exitDecisionOutcome(ctx) {} 66 | 67 | // Enter a parse tree produced by MADRParser#prosAndConsOfOptions. 68 | enterProsAndConsOfOptions(ctx) {} 69 | 70 | // Exit a parse tree produced by MADRParser#prosAndConsOfOptions. 71 | exitProsAndConsOfOptions(ctx) {} 72 | 73 | // Enter a parse tree produced by MADRParser#optionSection. 74 | enterOptionSection(ctx) {} 75 | 76 | // Exit a parse tree produced by MADRParser#optionSection. 77 | exitOptionSection(ctx) {} 78 | 79 | // Enter a parse tree produced by MADRParser#chosenOptionAndExplanation. 80 | enterChosenOptionAndExplanation(ctx) {} 81 | 82 | // Exit a parse tree produced by MADRParser#chosenOptionAndExplanation. 83 | exitChosenOptionAndExplanation(ctx) {} 84 | 85 | // Enter a parse tree produced by MADRParser#positiveConsequences. 86 | enterPositiveConsequences(ctx) {} 87 | 88 | // Exit a parse tree produced by MADRParser#positiveConsequences. 89 | exitPositiveConsequences(ctx) {} 90 | 91 | // Enter a parse tree produced by MADRParser#negativeConsequences. 92 | enterNegativeConsequences(ctx) {} 93 | 94 | // Exit a parse tree produced by MADRParser#negativeConsequences. 95 | exitNegativeConsequences(ctx) {} 96 | 97 | // Enter a parse tree produced by MADRParser#optionTitle. 98 | enterOptionTitle(ctx) {} 99 | 100 | // Exit a parse tree produced by MADRParser#optionTitle. 101 | exitOptionTitle(ctx) {} 102 | 103 | // Enter a parse tree produced by MADRParser#optionDescription. 104 | enterOptionDescription(ctx) {} 105 | 106 | // Exit a parse tree produced by MADRParser#optionDescription. 107 | exitOptionDescription(ctx) {} 108 | 109 | // Enter a parse tree produced by MADRParser#prolist. 110 | enterProlist(ctx) {} 111 | 112 | // Exit a parse tree produced by MADRParser#prolist. 113 | exitProlist(ctx) {} 114 | 115 | // Enter a parse tree produced by MADRParser#conlist. 116 | enterConlist(ctx) {} 117 | 118 | // Exit a parse tree produced by MADRParser#conlist. 119 | exitConlist(ctx) {} 120 | 121 | // Enter a parse tree produced by MADRParser#links. 122 | enterLinks(ctx) {} 123 | 124 | // Exit a parse tree produced by MADRParser#links. 125 | exitLinks(ctx) {} 126 | 127 | // Enter a parse tree produced by MADRParser#list. 128 | enterList(ctx) {} 129 | 130 | // Exit a parse tree produced by MADRParser#list. 131 | exitList(ctx) {} 132 | 133 | // Enter a parse tree produced by MADRParser#textLine. 134 | enterTextLine(ctx) {} 135 | 136 | // Exit a parse tree produced by MADRParser#textLine. 137 | exitTextLine(ctx) {} 138 | 139 | // Enter a parse tree produced by MADRParser#multilineText. 140 | enterMultilineText(ctx) {} 141 | 142 | // Exit a parse tree produced by MADRParser#multilineText. 143 | exitMultilineText(ctx) {} 144 | 145 | // Enter a parse tree produced by MADRParser#any. 146 | enterAny(ctx) {} 147 | 148 | // Exit a parse tree produced by MADRParser#any. 149 | exitAny(ctx) {} 150 | 151 | // Enter a parse tree produced by MADRParser#wslb. 152 | enterWslb(ctx) {} 153 | 154 | // Exit a parse tree produced by MADRParser#wslb. 155 | exitWslb(ctx) {} 156 | 157 | // Enter a parse tree produced by MADRParser#wslbs. 158 | enterWslbs(ctx) {} 159 | 160 | // Exit a parse tree produced by MADRParser#wslbs. 161 | exitWslbs(ctx) {} 162 | } 163 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ADR-Manager ![General cypress report](https://github.com/adr/adr-manager/workflows/General%20cypress%20report/badge.svg?branch=cypress-integration) [![GitHub license](https://img.shields.io/github/license/adr/adr-manager)](https://github.com/adr/adr-manager/blob/main/LICENSE) [![GitHub last commit](https://img.shields.io/github/last-commit/adr/adr-manager)](https://github.com/adr/adr-manager/commits/main) [![GitHub issues](https://img.shields.io/github/issues/adr/adr-manager)](https://github.com/adr/adr-manager/issues) [![GitHub stars](https://img.shields.io/github/stars/adr/adr-manager)](https://github.com/adr/adr-manager/stargazers) 2 | 3 | > A web-based application for the efficient creation and management of [architectural decision records (ADRs)](https://adr.github.io) in Markdown (MADR) 4 | 5 | ## Description 6 | 7 | [MADR](https://adr.github.io/madr/) is a Markdown template for quickly capturing architectural decisions. 8 | It offers a naming scheme and template to keep the layout of recorded decisions consistent. 9 | Each decision is stored in a separate file. 10 | The ADR Manager currently only supports the management of MADRs stored in the folder `docs/adr` in GitHub repositories. 11 | 12 | ## Quick Start 13 | 14 | You can find the tool at https://adr.github.io/adr-manager. 15 | 16 | ## Supported Browsers 17 | 18 | Currently, the tool has been successfully tested in Chrome, Firefox, and Opera. 19 | 20 | ### Usage 21 | 22 | 1. After opening the tool, connect to GitHub. The tool needs your permission to access your GitHub repositories and email address. 23 | 2. Add a GitHub repository. If your account does not have access to a repository with MADRs, you can simply fork one, e.g., or . 24 | 3. Now, you can edit any files in `docs/adr` of the GitHub repository. 25 | Edit existing ADRs or create new ones. 26 | One of the most important features is the MADR Editor that allows you to quickly draft a MADR while ensuring a consistent format. 27 | ![This is the MADR editor in advanced mode.](docs/img/editor-screenshot.png) 28 | 4. Do not forget to push your changes to GitHub, once you are done with editing the files. 29 | 30 | Some technical notes: 31 | 32 | - The `authID` (which enables the connection to GitHub) and changes to ADRs are stored in the local storage. 33 | That way they are not lost when you reload the page or restart the browser. 34 | However, changes will be lost when you either 35 | - Clear local storage or 36 | - Press the `Disconnect` button. 37 | - The general idea is that you directly push your changes to GitHub after editing. 38 | - During development, we may remove permissions for the OAuth App from time to time. 39 | Do not be surprised, if you have to give permissions repeatedly. 40 | 41 | ## Development 42 | 43 | ### Prerequisites 44 | 45 | - Node.js and npm 46 | - A GitHub account with access to a repository with MADRs 47 | 48 | ### Installation 49 | 50 | To run the project locally, follow these steps: 51 | 52 | 1. Clone this repository. 53 | 2. Install dependencies with `npm install`. 54 | 3. Compile and start the application with `npm start`. 55 | 56 | Note that, even when you run it locally, you need to connect to GitHub to use any functionality. 57 | 58 | ### Using End-2-End Tests Locally 59 | 60 | We use [Cypress](https://www.cypress.io/) for e2e testing. 61 | The CI pipeline provides the necessary Oauth `authId` as an ENV variable. 62 | Locally, however, you'll need to provide one yourself. 63 | You can either set `CYPRESS_OAUTH_E2E_AUTH_ID` and `CYPRESS_USER` containing the `authId` and `user` or create a `cypress.env.json` file and fill it with the following content: 64 | 65 | ```json 66 | { 67 | "OAUTH_E2E_AUTH_ID": "*********", 68 | "USER": "***********" 69 | } 70 | ``` 71 | 72 | The value of `OAUTH_E2E_AUTH_ID` and `USER` needs to be a valid `authId` and `user` from an active OAuth session, which you can obtain in the local storage (Chrome developer console -> Application -> Storage -> Local Storage -> `http://localhost:8080` -> `authId`, `user`) 73 | The involved GitHub account also needs to have developer access to the repo `adr/adr-test-repository-empty`. 74 | Lastly, don't forget to start the app before running the e2e tests (`npm start`). 75 | 76 | ### Useful Commands 77 | 78 | The following commands are useful for development: 79 | 80 | ```bash 81 | # install dependencies 82 | npm install 83 | 84 | # build and start with hot-reload for development 85 | npm start 86 | 87 | # build and minify for production 88 | npm run build 89 | 90 | # run unit tests 91 | npm test 92 | 93 | # run e2e tests 94 | npm run e2e:test 95 | 96 | # open cypress GUI for e2e tests 97 | npx cypress open 98 | 99 | # run a single e2e test 100 | npx cypress run --spec ./cypress/e2e/adrManagerTest/ 101 | 102 | # format code with prettier (do this before you commit and push) 103 | npm run format 104 | ``` 105 | 106 | ### Backend Setup 107 | 108 | The project uses [OAuth] for the authentication to GitHub. 109 | If you do not want to use this instance, you can easily set up your own by following these steps: 110 | 111 | 1. Create an OAuth application on GitHub (see [here](https://docs.github.com/en/github-ae@latest/developers/apps/creating-an-oauth-app)). 112 | - Copy the Client ID and Client Secret of the app (you'll need them later). 113 | 2. Create a Github app on Firebase and in its configurations, set the Client ID and Client Secret as copied from the above Github app 114 | 115 | - Set the callback URL in Github Oauth app configuration to the one provided by Firebase. 116 | 117 | ## Project Context 118 | 119 | This project was started as an undergraduate research project at the Institute of Software Engineering of the University of Stuttgart, Germany. 120 | It was also submitted to the [ICSE Score Contest 2021](https://conf.researchr.org/home/icse-2021/score-2021). 121 | Since then, it has been given over to the [ADR organization on GitHub](https://github.com/adr), where it is maintained and extended. 122 | -------------------------------------------------------------------------------- /src/components/EditorMadrStatusDateDecidersStory.vue: -------------------------------------------------------------------------------- 1 | 78 | 79 | 157 | 158 | 164 | -------------------------------------------------------------------------------- /src/components/Editor.vue: -------------------------------------------------------------------------------- 1 | 68 | 69 | 183 | -------------------------------------------------------------------------------- /src/components/EditorMadr.vue: -------------------------------------------------------------------------------- 1 | 117 | 118 | 192 | -------------------------------------------------------------------------------- /src/components/EditorMadrList.vue: -------------------------------------------------------------------------------- 1 | 2 | 81 | 82 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /docs/evaluation/adr-parsing/cypress/integration/EmpiricalAnalysis.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | /* eslint-disable no-undef */ 3 | 4 | var REPO_NAMES = [ 5 | // These repositories are public, i.e., this test relies 6 | // on the functionality to load public repositories 7 | // into the ADR-Manager using the search field. 8 | "adr-manager-anonymous/adr-j", 9 | "adr-manager-anonymous/adr-log", 10 | // "adr-manager-anonymous/adr-manager", 11 | "adr-manager-anonymous/adr-tools", 12 | "adr-manager-anonymous/architectural-decision-records", 13 | "adr-manager-anonymous/blueprint", 14 | "adr-manager-anonymous/cloud-on-k8s", 15 | "adr-manager-anonymous/complaitkubernetes", 16 | "adr-manager-anonymous/core", 17 | "adr-manager-anonymous/digital-paper-edit-client", 18 | "adr-manager-anonymous/digital-paper-edit-firebase", 19 | "adr-manager-anonymous/docker-texlive", 20 | "adr-manager-anonymous/docs", 21 | "adr-manager-anonymous/e-adr", 22 | "adr-manager-anonymous/ecar-charge-pricing", 23 | "adr-manager-anonymous/experimenter", 24 | "adr-manager-anonymous/fxa", 25 | "adr-manager-anonymous/gadr", 26 | "adr-manager-anonymous/island.is", 27 | "adr-manager-anonymous/jabref", 28 | "adr-manager-anonymous/log4brains", 29 | "adr-manager-anonymous/madr", 30 | "adr-manager-anonymous/monocle", 31 | "adr-manager-anonymous/nodejs.dev", 32 | "adr-manager-anonymous/odh-manifests", 33 | "adr-manager-anonymous/python-tuf", 34 | "adr-manager-anonymous/raiden", 35 | "adr-manager-anonymous/raiden-services", 36 | "adr-manager-anonymous/Sylius", 37 | "adr-manager-anonymous/tuleap", 38 | "adr-manager-anonymous/winery" /* 39 | "adr/madr", 40 | "npryce/adr-tools", 41 | "api-platform/core", 42 | "archivematica/archivematica-architectural-decisions", // <- Fixed typo 43 | "bbc/digital-paper-edit-client", 44 | "bbc/digital-paper-edit-firebase", 45 | "change-metrics/monocle", 46 | "dante-ev/docker-texlive", 47 | "eclipse/winery", 48 | "elastic/cloud-on-k8s", 49 | "elastisys/compliantkubernetes", 50 | "Enalean/tuleap", 51 | "island-is/island.is", // <- Fixed typo 52 | "JabRef/jabref", 53 | "knizamov/ecar-charge-pricing", 54 | "mozilla/experimenter", 55 | "mozilla/fxa", 56 | "nodejs/nodejs.dev", 57 | "opendatahub-io/odh-manifests", 58 | "operate-first/blueprint", 59 | "opinionated-digital-center/architecture-decision-records", 60 | "raiden-network/raiden", 61 | "raiden-network/raiden-services", 62 | "Sylius/Sylius", // <- Fixed typo 63 | "theupdateframework/python-tuf", 64 | "thomvaill/log4brains", 65 | "UST-MICO/docs", */ 66 | ] 67 | 68 | context("Empirically test if ADRs can be opened by the ADR-Manager and count how many are accepted by the parser without the 'Diff' screen.", () => { 69 | 70 | beforeEach(() => { 71 | cy.visit("http://localhost:8080/#/manager"); 72 | window.localStorage.clear(); 73 | window.localStorage.setItem( 74 | "authId", 75 | Cypress.env("OAUTH_E2E_AUTH_ID") 76 | ); 77 | 78 | cy.intercept("GET", "**/user/repos**").as("getRepos"); 79 | cy.get("[data-cy=addRepo]").click(); 80 | cy.wait("@getRepos") 81 | .its("response.statusCode") 82 | .should("eq", 200); 83 | 84 | }); 85 | 86 | for (var j = 0; j < REPO_NAMES.length; j++) { 87 | let i = j // Store loop variable into the local scope. 88 | function testDiff() { 89 | if (i == 0) { 90 | // In the first iteration create counters 91 | cy.writeFile("cypress/fixtures/CounterDiffAllRepos.json", { 92 | counter: 0 93 | }); 94 | cy.writeFile("cypress/fixtures/CounterAdrsAllRepos.json", { 95 | counter: 0 96 | }); 97 | cy.writeFile("cypress/fixtures/CounterDiffPerRepo.json", { 98 | [REPO_NAMES[i]]: 0 99 | }); 100 | cy.writeFile("cypress/fixtures/CounterAdrsPerRepo.json", { 101 | [REPO_NAMES[i]]: 0 102 | }); 103 | } else { 104 | // Add repo-specific counters 105 | cy.readFile( 106 | "cypress/fixtures/CounterAdrsPerRepo.json" 107 | ).then(counter => { 108 | counter[REPO_NAMES[i]] = 0; 109 | cy.writeFile("cypress/fixtures/CounterAdrsPerRepo.json", counter); 110 | }); 111 | cy.readFile( 112 | "cypress/fixtures/CounterDiffPerRepo.json" 113 | ).then(counter => { 114 | counter[REPO_NAMES[i]] = 0; 115 | cy.writeFile("cypress/fixtures/CounterDiffPerRepo.json", counter); 116 | }); 117 | } 118 | // add the repo 119 | cy.log(REPO_NAMES[i]); 120 | cy.get("[data-cy=search-field-for-adding-repository]") 121 | .type("https://github.com/" + REPO_NAMES[i]); 122 | cy.contains(REPO_NAMES[i]) 123 | .click(); 124 | cy.get("[data-cy=addRepoDialog]").click({ force: true }); 125 | cy.intercept("GET", "**/repos**").as("showRepos"); 126 | cy.wait("@showRepos", { timeout: 10000 }); 127 | 128 | // Iterate through the ADRs and count how often the Convert Tab is opened. 129 | cy.get("[data-cy=adrList]") 130 | .each(($adr, index, $adrs) => { 131 | cy.get($adr).click(); 132 | cy.get("header").then($a => { 133 | cy.log("text", $a.text()); 134 | if ($a.text().includes("Convert")) { 135 | cy.log("Convert Tab opened"); 136 | // Update Diff counters 137 | cy.readFile( 138 | "cypress/fixtures/CounterDiffPerRepo.json" 139 | ).then(counter => { 140 | counter[REPO_NAMES[i]] = counter[REPO_NAMES[i]] + 1; 141 | cy.writeFile( 142 | "cypress/fixtures/CounterDiffPerRepo.json", 143 | counter 144 | ); 145 | }); 146 | cy.readFile( 147 | "cypress/fixtures/CounterDiffAllRepos.json" 148 | ).then(counter => { 149 | counter.counter = counter.counter + 1; 150 | cy.writeFile( 151 | "cypress/fixtures/CounterDiffAllRepos.json", 152 | counter 153 | ); 154 | }); 155 | } else cy.log(i + " not visible"); 156 | 157 | // Update ADR counters 158 | cy.readFile( 159 | "cypress/fixtures/CounterAdrsPerRepo.json" 160 | ).then(counter => { 161 | counter[REPO_NAMES[i]] = counter[REPO_NAMES[i]] + 1; 162 | cy.writeFile( 163 | "cypress/fixtures/CounterAdrsPerRepo.json", 164 | counter 165 | ); 166 | }); 167 | cy.readFile( 168 | "cypress/fixtures/CounterAdrsAllRepos.json" 169 | ).then(counter => { 170 | counter.counter = counter.counter + 1; 171 | cy.writeFile( 172 | "cypress/fixtures/CounterAdrsAllRepos.json", 173 | counter 174 | ); 175 | }); 176 | }); 177 | }); 178 | cy.get("[data-cy=removeRepo]").click(); 179 | cy.get("[data-cy=removeRepoBtn]").click(); 180 | 181 | cy.readFile("cypress/fixtures/CounterDiffPerRepo.json").then( 182 | counter => { 183 | cy.log("diff", counter.counter); 184 | } 185 | ); 186 | cy.readFile("cypress/fixtures/CounterAdrsPerRepo.json").then( 187 | counter => { 188 | cy.log("adr", counter.counter); 189 | } 190 | ); 191 | } 192 | 193 | // Create the actual tests 194 | it("Test ADRs of the repository " + REPO_NAMES[i], testDiff); 195 | } 196 | }); 197 | -------------------------------------------------------------------------------- /docs/evaluation/adr-parsing/adr-parsing-manual-results.csv: -------------------------------------------------------------------------------- 1 | Name,Original Repository,Forked Repository Link,ADR directory,Total ADRs,Read ADRs,Old MADR format,"Text before ""Status""","Header ""Context and Problem Statement"" missing",Format error in Decision Outcome,Additional line breaks,Options not sorted consistently,Option list formatted differently,Ordering of headings differently,Spurious white spaces,Template not followed,Inconsistent Casing,Markdown Parser deficiencies,Other,Sum,,Note 2 | ADR Tools (adr-j),https://github.com/adr/adr-j,https://github.com/adr-manager-anonymous/adr-j,https://github.com/adr-manager-anonymous/adr-j/tree/master/doc/adr,6,0,6,,,,,,,,,,,,,6,,Used MADR 1.x format 3 | adr-log,https://github.com/adr/adr-log,https://github.com/adr-manager-anonymous/adr-log,https://github.com/adr-manager-anonymous/adr-log/tree/master/docs/adr,5,0,5,,,,,,,,,,,,,5,,Used MADR 1.x format 4 | adr-manager,https://github.com/adr-manager-anonymous/adr-manager,https://github.com/adr-manager-anonymous/adr-manager,https://github.com/adr/adr-manager/tree/main/docs/adr,5,5,,,,,,,,,,,,,,5,, 5 | Opinionated Digital Center's Architecture Decision Record Repository,https://github.com/opinionated-digital-center/architecture-decision-records,https://github.com/adr-manager-anonymous/architecture-decision-records,https://github.com/adr-manager-anonymous/architecture-decision-records/tree/master/docs/adr,9,3,2,2,,1,,,,,,,,1,0,9,,Outcome not in quotes; Markdown lists cannot be parsed 6 | Archivematica Architectural Decision Records,https://github.com/archivematica/archivematica-architectural-decisions,https://github.com/adr-manager-anonymous/archivematica-architectural-decisions,https://github.com/adr-manager-anonymous/archivematica-architectural-decisions,11,0,,,,,,,,,,,,,11,11,,contained in root folder 7 | Operate First Blueprint,https://github.com/operate-first/blueprint,https://github.com/operate-first/blueprint,https://github.com/operate-first/blueprint/tree/main/adr,19,2,,,1,1,,,14,1,,,,,,19,,"Options not as enumeration, not itemized; Description of option mixed with list 8 | ; comments before option list; Options as headings; pro/con format not followed; new Heading ""Prequreisites""" 9 | Elastic Cloud on Kubernetes (ECK),https://github.com/elastic/cloud-on-k8s,https://github.com/adr-manager-anonymous/cloud-on-k8s,https://github.com/adr-manager-anonymous/cloud-on-k8s/tree/master/docs/design,12,0,,,,,,,,,,,,,12,12,,"ADRs in directory ""docs/design"", sub directories; concidered options as headings" 10 | Compliant Kubernetes Documentation,https://github.com/elastisys/compliantkubernetes,https://github.com/adr-manager-anonymous/compliantkubernetess,https://github.com/adr-manager-anonymous/compliantkubernetes/tree/main/docs/adr,17,1,,,,8,,1,1,,,4,,2,,17,,"Outcome plain text; More section ""Recommendations to Operators"" / ""Other Considerations""; One 'correct' outcome could not be parsed" 11 | API Platform Core,https://github.com/api-platform/core,https://github.com/adr-manager-anonymous/core,https://github.com/adr-manager-anonymous/core/tree/main/docs/adr,4,0,,,,4,,,,,,,,,,4,,"Addiional item ""Implementation"" in the header" 12 | Digital Paper Edit - Client,https://github.com/bbc/digital-paper-edit-client/tree/master/docs/ADR,https://github.com/adr-manager-anonymous/digital-paper-edit-client,https://github.com/adr-manager-anonymous/digital-paper-edit-client/tree/master/docs/ADR,8,0,8,,,,,,,,,,,,,8,,"Commented out text, ..." 13 | Digital Paper Edit - Firebase,https://github.com/bbc/digital-paper-edit-firebase,https://github.com/adr-manager-anonymous/digital-paper-edit-firebase,https://github.com/adr-manager-anonymous/digital-paper-edit-firebase/tree/master/docs/ADR,12,0,12,,,,,,,,,,,,,12,,"Commented out text, ..." 14 | Docker image for texlive,https://github.com/dante-ev/docker-texlive/tree/main/docs/adr,https://github.com/adr-manager-anonymous/docker-texlive,https://github.com/adr-manager-anonymous/docker-texlive/tree/main/docs/adr,3,2,,,,,,,,,1,,,,,3,, 15 | A Management System for Microservice Compositions,https://github.com/UST-MICO/docs,https://github.com/adr-manager-anonymous/docs,https://github.com/adr-manager-anonymous/docs/tree/master/adr,28,0,,,,16,,,3,,2,2,,,5,28,,Casing in the headings; general parsing error 16 | Embedded Architectural Decision Records,https://github.com/adr/e-adr,https://github.com/adr-manager-anonymous/e-adr,https://github.com/adr-manager-anonymous/e-adr/tree/main/docs/adr,3,0,3,,,,,,,,,,,,,3,,Decision outcome not properly formatted 17 | eCar charge pricing,https://github.com/knizamov/ecar-charge-pricing,https://github.com/adr-manager-anonymous/ecar-charge-pricing,https://github.com/adr-manager-anonymous/ecar-charge-pricing/tree/master/docs/decisions_log,1,0,,,,,,,,,,1,,,,1,,Decision in outher directory 18 | Mozilla Experimenter,https://github.com/mozilla/experimenter,https://github.com/adr-manager-anonymous/experimenter,https://github.com/adr-manager-anonymous/experimenter/tree/main/app/experimenter/docs/adrs,7,0,,,,,,,,,,7,,,,7,,Pros and cons formatted differently 19 | Firefox Accounts,https://github.com/mozilla/fxa,https://github.com/adr-manager-anonymous/fxa,https://github.com/adr-manager-anonymous/fxa/tree/main/docs/adr,28,1,,,,,,1,4,,,17,1,2,2,28,,"""TypeScript"" versus ""Typescript""; Option name usage; Markdown list handling; pros and cons summarized; pros and cons formatted differently; options not listed" 20 | Generalized Architectural Decision Records,https://github.com/adr/gadr,https://github.com/adr-manager-anonymous/gadr,https://github.com/adr-manager-anonymous/gadr/tree/master/docs/adr,2,2,,,,,,,,,,,,,,2,, 21 | Ísland.is,https://github.com/island-is/island.is,https://github.com/adr-manager-anonymous/island.is,https://github.com/adr-manager-anonymous/island.is/tree/main/handbook/technical-overview/adr,15,0,,,,3,,,,,,10,,1,1,15,,"Link included in option description headings, Parser issue with lists, ""Pos and Cons of other Options""; Grouping of options, Option argument neither pro nor con; intro text at option//pro/con section; options are ""some mixtures of these ...""; ""Great"" additionally to ""Good""" 22 | JabRef,https://github.com/JabRef/jabref/tree/main/docs/adr,https://github.com/adr-manager-anonymous/jabref,https://github.com/adr-manager-anonymous/jabref/tree/main/docs/adr,24,15,1,,,1,,1,,,,2,,4,,24,,"markdownlint-disable-file rules are not supported; ""Other alternatives are listed at""; MD linebreaks not treated in all cases; Slight mismatch on option title; Double space does not work" 23 | log4brains,https://github.com/thomvaill/log4brains/tree/master/docs/adr,https://github.com/adr-manager-anonymous/log4brains,https://github.com/adr-manager-anonymous/log4brains/tree/master/docs/adr,9,0,,,,2,,,,,,3,,4,,9,,Options substructured in sub options; lists are not correctly parsed 24 | MADR,https://github.com/adr/madr,https://github.com/adr-manager-anonymous/madr,https://github.com/adr-manager-anonymous/madr/tree/main/docs/decisions,13,8,,,1,4,,,,,,,,,,13,,"Uses different template, where the option description and the option list is merged" 25 | Monocle,https://github.com/change-metrics/monocle,https://github.com/adr-manager-anonymous/monocle,https://github.com/adr-manager-anonymous/monocle/tree/master/doc/adr,11,3,,,,3,,,,,,2,,1,,9,,Decision drivers not a list 26 | node.js,https://github.com/nodejs/nodejs.dev,https://github.com/adr-manager-anonymous/nodejs.dev,https://github.com/adr-manager-anonymous/nodejs.dev/tree/main/docs/adr,2,0,,,,,,,,,,1,,1,,2,,Multilinelists 27 | Open Data Hub Manifests,https://github.com/opendatahub-io/odh-manifests,https://github.com/adr-manager-anonymous/odh-manifests,https://github.com/adr-manager-anonymous/odh-manifests/tree/master/docs/adr,1,0,,,,,,,,,,,,1,,1,, 28 | A Framework for Securing Software Update Systems,https://github.com/theupdateframework/python-tuf,https://github.com/adr-manager-anonymous/python-tuf,https://github.com/adr-manager-anonymous/python-tuf/tree/develop/docs/adr,9,1,,,,,,,,,,1,,7,,9,, 29 | Raiden,https://github.com/raiden-network/raiden,https://github.com/adr-manager-anonymous/raiden,https://github.com/adr-manager-anonymous/raiden/tree/develop/docs/adr,10,0,,,,,,,,,,,,,10,10,,Uses ReStructured Text 30 | Raden Services,https://github.com/raiden-network/raiden-services,https://github.com/adr-manager-anonymous/raiden-services,https://github.com/adr-manager-anonymous/raiden-services/tree/master/adr,3,0,,,,,,,3,,,,,,,3,,Numbered List used 31 | Sylius,https://github.com/Sylius/Sylius,https://github.com/adr-manager-anonymous/Sylius,https://github.com/adr-manager-anonymous/Sylius/tree/master/adr,12,0,,,,,,,8,,,3,,1,,12,,Options are headings (merged pros/cons into option list); No outcome 32 | Tuleap,https://github.com/Enalean/tuleap,https://github.com/adr-manager-anonymous/tuleap,https://github.com/adr-manager-anonymous/tuleap/tree/master/adr,7,1,,,,,,,,,,1,,5,,7,, 33 | Eclipse Winery,https://github.com/eclipse/winery,https://github.com/adr-manager-anonymous/winery,https://github.com/adr-manager-anonymous/winery/tree/master/docs/adr,32,2,11,,,12,,,,,,4,,,3,32,,"Cannot parse ""## License"" at the end of the MD file; ""Usage"" section; Multiple ADRs in one MD file; pros/cons" 34 | ,,,,,,,,,,,,,,,,,,,,, 35 | ,,,,,,,,,,,,,,,,,,,,, 36 | Sum,,,,328,46,48,2,2,55,0,3,33,1,3,58,1,30,44,326,, --------------------------------------------------------------------------------