diff --git a/.github/workflows/time-tracker-ui-cd-prod.yml b/.github/workflows/time-tracker-ui-cd-prod.yml
new file mode 100644
index 000000000..13e7f6b86
--- /dev/null
+++ b/.github/workflows/time-tracker-ui-cd-prod.yml
@@ -0,0 +1,52 @@
+name: time-tracker-ui-cd-prod
+
+on:
+ release:
+ types:
+ - published
+
+jobs:
+ cd:
+ runs-on: ubuntu-latest
+ env:
+ TF_WORKSPACE: prod
+ WORKING_DIR: infrastructure/
+ ARM_CLIENT_ID: ${{secrets.TF_ARM_CLIENT_ID}}
+ ARM_CLIENT_SECRET: ${{secrets.TF_ARM_CLIENT_SECRET}}
+ ARM_SUBSCRIPTION_ID: ${{secrets.TF_ARM_SUBSCRIPTION_ID}}
+ ARM_TENANT_ID: ${{secrets.TF_ARM_TENANT_ID}}
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Get the release_version
+ run: |
+ echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
+ echo $RELEASE_VERSION
+ - name: Login to azure
+ uses: Azure/login@v1
+ with:
+ creds: ${{ secrets.AZURE_CREDENTIALS }}
+
+ - name: Build the docker image
+ run: make build
+
+ - name: Publish docker image to prod azure container registry
+ run: |
+ make login publish acr=timetrackerserviceprodregistry image_tag=$RELEASE_VERSION
+ - name: Setup terraform
+ uses: hashicorp/setup-terraform@v1
+
+ - name: Authenticate with the TF modules repository
+ uses: webfactory/ssh-agent@v0.5.4
+ with:
+ ssh-private-key: ${{ secrets.INFRA_TERRAFORM_MODULES_SSH_PRIV_KEY }}
+
+ - name: Terraform Init
+ working-directory: ${{ env.WORKING_DIR }}
+ run: terraform init
+
+ - name: Terraform Apply
+ working-directory: ${{ env.WORKING_DIR }}
+ run: terraform apply -lock=false -var-file="${{ env.TF_WORKSPACE }}.tfvars" -var "image_tag=$RELEASE_VERSION" -auto-approve
diff --git a/.github/workflows/time-tracker-ui-cd-stage.yml b/.github/workflows/time-tracker-ui-cd-stage.yml
new file mode 100644
index 000000000..ab736d69d
--- /dev/null
+++ b/.github/workflows/time-tracker-ui-cd-stage.yml
@@ -0,0 +1,45 @@
+name: time-tracker-ui-cd-stage
+
+on:
+ push:
+ tags:
+ - 'v*.*.*'
+
+jobs:
+ cd:
+ runs-on: ubuntu-latest
+ env:
+ TF_WORKSPACE: stage
+ WORKING_DIR: infrastructure/
+ ARM_CLIENT_ID: ${{secrets.TF_ARM_CLIENT_ID}}
+ ARM_CLIENT_SECRET: ${{secrets.TF_ARM_CLIENT_SECRET}}
+ ARM_SUBSCRIPTION_ID: ${{secrets.TF_ARM_SUBSCRIPTION_ID}}
+ ARM_TENANT_ID: ${{secrets.TF_ARM_TENANT_ID}}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ - name: Get the release_version
+ run: |
+ echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
+ echo $RELEASE_VERSION
+ - name: Login to azure
+ uses: Azure/login@v1
+ with:
+ creds: ${{ secrets.AZURE_CREDENTIALS }}
+ - name: Build the docker image
+ run: make build
+ - name: Publish docker image to stage azure container registry
+ run: |
+ make login publish acr=timetrackerservicestageregistry image_tag=$RELEASE_VERSION
+ - name: Setup terraform
+ uses: hashicorp/setup-terraform@v1
+ - name: Authenticate with the TF modules repository
+ uses: webfactory/ssh-agent@v0.5.4
+ with:
+ ssh-private-key: ${{ secrets.INFRA_TERRAFORM_MODULES_SSH_PRIV_KEY }}
+ - name: Terraform Init
+ working-directory: ${{ env.WORKING_DIR }}
+ run: terraform init
+ - name: Terraform Apply
+ working-directory: ${{ env.WORKING_DIR }}
+ run: terraform apply -lock=false -var-file="${{ env.TF_WORKSPACE }}.tfvars" -var "image_tag=$RELEASE_VERSION" -auto-approve
diff --git a/.github/workflows/time-tracker-ui-ci.yml b/.github/workflows/time-tracker-ui-ci.yml
new file mode 100644
index 000000000..92b8985b3
--- /dev/null
+++ b/.github/workflows/time-tracker-ui-ci.yml
@@ -0,0 +1,138 @@
+name: time-tracker-ui-ci
+
+on:
+ push:
+ branches:
+ - master
+
+ pull_request:
+ branches:
+ - master
+
+jobs:
+ ci:
+ runs-on: ubuntu-latest
+ env:
+ WORKING_DIR: infrastructure/
+ DB_CONNECTION: ${{ secrets.DB_CONNECTION }}
+ ARM_CLIENT_ID: ${{secrets.TF_ARM_CLIENT_ID}}
+ ARM_CLIENT_SECRET: ${{secrets.TF_ARM_CLIENT_SECRET}}
+ ARM_SUBSCRIPTION_ID: ${{secrets.TF_ARM_SUBSCRIPTION_ID}}
+ ARM_TENANT_ID: ${{secrets.TF_ARM_TENANT_ID}}
+ strategy:
+ max-parallel: 5
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Authenticate with the TF modules repository
+ uses: webfactory/ssh-agent@v0.5.4
+ with:
+ ssh-private-key: ${{ secrets.INFRA_TERRAFORM_MODULES_SSH_PRIV_KEY }}
+
+ - name: build docker
+ run: make build
+
+ - name: Inject Secrets
+ env:
+ SCOPES: ${{ secrets.SCOPES }}
+ CLIENT_ID: ${{ secrets.CLIENT_ID }}
+ AUTHORITY: ${{ secrets.AUTHORITY }}
+ STACK_EXCHANGE_ID: ${{ secrets.STACK_EXCHANGE_ID }}
+ STACK_EXCHANGE_ACCESS_TOKEN: ${{ secrets.STACK_EXCHANGE_ACCESS_TOKEN }}
+ AZURE_APP_CONFIGURATION_CONNECTION_STRING: ${{ secrets.AZURE_APP_CONFIGURATION_CONNECTION_STRING }}
+ run: |
+ chmod +x ./scripts/populate-keys.sh
+ sh ./scripts/populate-keys.sh
+
+ - name: Running tests
+ run: |
+ chmod -R 777 ./$home
+ make test
+ - name: Generate coverage report
+ env:
+ CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
+ run: bash <(curl -s https://codecov.io/bash)
+
+ - name: Setup terraform
+ uses: hashicorp/setup-terraform@v1
+
+ - name: 'Terraform Init'
+ id: init
+ working-directory: ./${{ env.WORKING_DIR }}
+ run: terraform init
+
+ - name: 'Terraform validate'
+ id: validate
+ working-directory: ./${{ env.WORKING_DIR }}
+ run: terraform validate
+
+ - name: Terraform Plan Stage
+ id: plan-stage
+ if: github.event_name == 'pull_request'
+ run: terraform plan -var-file=${{ env.TF_WORKSPACE }}.tfvars -var image_tag=latest -no-color
+ continue-on-error: true
+ working-directory: ./${{ env.WORKING_DIR }}
+ env:
+ TF_WORKSPACE: stage
+
+ - name: Terraform Plan Prod
+ id: plan-prod
+ if: github.event_name == 'pull_request'
+ run: terraform plan -var-file=${{ env.TF_WORKSPACE }}.tfvars -no-color
+ continue-on-error: true
+ working-directory: ./${{ env.WORKING_DIR }}
+ env:
+ TF_WORKSPACE: prod
+
+ - name: Update Pull Request with Stage Plan
+ uses: actions/github-script@0.9.0
+ if: github.event_name == 'pull_request'
+ env:
+ PLAN: "terraform\n${{ steps.plan-stage.outputs.stdout }}"
+ TF_WORKSPACE: stage
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const output = `#### [${{ env.WORKING_DIR }}][${{ env.TF_WORKSPACE }}] Terraform Plan 📖 \`${{ steps.plan-stage.outcome }}\`
+ Show Plan
+ \`\`\`\n
+ ${process.env.PLAN}
+ \`\`\`
+
+ *Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`;
+ github.issues.createComment({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ body: output
+ })
+ - name: Update Pull Request with Prod Plan
+ uses: actions/github-script@0.9.0
+ if: github.event_name == 'pull_request'
+ env:
+ PLAN: "terraform\n${{ steps.plan-prod.outputs.stdout }}"
+ TF_WORKSPACE: prod
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const output = `#### [${{ env.WORKING_DIR }}][${{ env.TF_WORKSPACE }}] Terraform Plan 📖 \`${{ steps.plan-prod.outcome }}\`
+ Show Plan
+ \`\`\`\n
+ ${process.env.PLAN}
+ \`\`\`
+
+ *Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`;
+ github.issues.createComment({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ body: output
+ })
+ - name: Terraform Plan Stage Status
+ if: steps.plan-stage.outcome == 'failure'
+ run: exit 1
+
+ - name: Terraform Plan Prod Status
+ if: steps.plan-prod.outcome == 'failure'
+ run: exit 1
diff --git a/Dockerfile b/Dockerfile
index 01a5d3727..7c2029c6c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -2,6 +2,33 @@ FROM node:14 AS development
ENV USERNAME timetracker
ENV HOME /home/${USERNAME}
+ENV CHROME_BIN /opt/google/chrome/google-chrome
+#Essential tools and xvfb
+RUN apt-get update && apt-get install -y \
+ software-properties-common \
+ unzip \
+ curl \
+ wget \
+ xvfb
+
+#Chrome browser to run the tests
+ARG CHROME_VERSION=65.0.3325.181
+RUN curl https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add \
+ && wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb \
+ && dpkg -i google-chrome-stable_current_amd64.deb || true
+RUN apt-get install -y -f \
+ && rm -rf /var/lib/apt/lists/*
+
+#Disable the SUID sandbox so that chrome can launch without being in a privileged container
+RUN dpkg-divert --add --rename --divert /opt/google/chrome/google-chrome.real /opt/google/chrome/google-chrome \
+ && echo "#! /bin/bash\nexec /opt/google/chrome/google-chrome.real --no-sandbox --disable-setuid-sandbox \"\$@\"" > /opt/google/chrome/google-chrome \
+ && chmod 755 /opt/google/chrome/google-chrome
+
+#Chrome Driver
+ARG CHROME_DRIVER_VERSION=2.37
+RUN mkdir -p /opt/selenium \
+ && curl http://chromedriver.storage.googleapis.com/$CHROME_DRIVER_VERSION/chromedriver_linux64.zip -o /opt/selenium/chromedriver_linux64.zip \
+ && cd /opt/selenium; unzip /opt/selenium/chromedriver_linux64.zip; rm -rf chromedriver_linux64.zip; ln -fs /opt/selenium/chromedriver /usr/local/bin/chromedriver;
RUN useradd -ms /bin/bash ${USERNAME}
@@ -9,6 +36,9 @@ WORKDIR ${HOME}/time-tracker-ui
COPY . .
RUN rm -f .env
RUN chown ${USERNAME}:${USERNAME} -R ${HOME}/time-tracker-ui
+RUN chmod -R 777 ${HOME}/time-tracker-ui
+
+
USER ${USERNAME}
RUN npm cache clean --force && npm install
diff --git a/Makefile b/Makefile
index d86ace915..7d77f85ad 100644
--- a/Makefile
+++ b/Makefile
@@ -27,11 +27,11 @@ logs: ## Show logs of timetracker_ui.
.PHONY: stop
stop: ## Stop container timetracker_ui.
- docker-compose stop
+ docker-compose stop
.PHONY: restart
restart: ## Restart container timetracker_ui.
- docker-compose stop
+ docker-compose stop
docker-compose up -d
.PHONY: remove
@@ -39,14 +39,19 @@ remove: ## Delete container timetracker_ui.
docker-compose down --volumes --remove-orphans --rmi local
.PHONY: test
-test: ## Run all tests on docker container timetracker_ui.
- docker-compose --env-file ./.env up -d
- docker exec -it timetracker_ui bash -c "npm run test"
+test: ## Run all tests on docker container timetracker_ui at the CLI.
+ docker-compose -f docker-compose.yml --env-file ./.env up -d
+ docker exec timetracker_ui bash -c "npm run ci-test"
+
+.PHONY: testdev
+testdev: ## Run all tests on docker container timetracker_ui at the Dev
+ docker-compose -f docker-compose.yml -f docker-compose.dev.yml --env-file ./.env up -d
+ docker exec timetracker_ui bash -c "npm run ci-test"
.PHONY: publish
-publish: ## Publish the container image timetracker_ui.
- docker tag timetracker_ui:latest $(registry_url)/timetracker_ui:latest
- docker push $(registry_url)/timetracker_ui:latest
+publish: require-acr-arg require-image_tag-arg ## Upload a docker image to an azure container registry acr= image_tag=
+ docker tag timetracker_api $(acr).azurecr.io/timetracker_api:$(image_tag)
+ docker push $(acr).azurecr.io/timetracker_api:$(image_tag)
.PHONY: build_prod
build_prod: ## Create docker image with dependencies needed for production.
@@ -54,7 +59,7 @@ build_prod: ## Create docker image with dependencies needed for production.
.PHONY: run_prod
run_prod: ## Execute timetracker_ui_prod docker container.
- docker run -d -p 4200:4200 --name timetracker_ui_prod timetracker_ui_prod
+ docker run -d -p 4200:4200 --name timetracker_ui_prod timetracker_ui_prod
.PHONY: remove_prod
remove_prod: ## Delete container timetracker_ui_pro.
@@ -63,9 +68,9 @@ remove_prod: ## Delete container timetracker_ui_pro.
.PHONY: publish_prod
publish_prod: ## Publish the container image timetracker_ui_prod.
- docker tag timetracker_ui_prod:latest $(registry_url)/timetracker_ui_prod:latest
- docker push $(registry_url)/timetracker_ui_prod:latest
+ docker tag timetracker_ui_prod:$(image_tag) $(registry_url)/timetracker_ui_prod:$(image_tag)
+ docker push $(registry_url)/timetracker_ui_prod:$(image_tag)
.PHONY: login
login: ## Login in respository of docker images.
- az acr login --name $(container_registry)
+ az acr login --name $(acr)
diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml
new file mode 100644
index 000000000..18535960c
--- /dev/null
+++ b/docker-compose.dev.yml
@@ -0,0 +1,13 @@
+version: '3.9'
+services:
+ time-tracker-ui:
+ user: root
+ volumes:
+ - ./src:/home/timetracker/time-tracker-ui/src/
+ - ./scripts:/home/timetracker/time-tracker-ui/scripts/
+ - ./e2e:/home/timetracker/time-tracker-ui/e2e/
+ - ./coverage:/home/timetracker/time-tracker-ui/coverage
+ - ./angular.json:/home/timetracker/time-tracker-ui/angular.json
+ - ./karma.conf.js:/home/timetracker/time-tracker-ui/karma.conf.js
+ - ./package.json:/home/timetracker/time-tracker-ui/package.json
+ - ./webpack.config.js:/home/timetracker/time-tracker-ui/webpack.config.js
diff --git a/docker-compose.yml b/docker-compose.yml
index d7516c1a1..61bd1ca44 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -11,6 +11,7 @@ services:
- 4200:4200
- 9876:9876
environment:
+ CHROME_BIN: /opt/google/chrome/google-chrome
AUTHORITY: ${AUTHORITY}
CLIENT_ID: ${CLIENT_ID}
SCOPES: ${SCOPES}
@@ -20,12 +21,4 @@ services:
AUTHORITY_JSON: ${AUTHORITY_JSON}
CLIENT_ID_JSON: ${CLIENT_ID_JSON}
SCOPES_JSON: ${SCOPES_JSON}
- volumes:
- - ./src:/home/timetracker/time-tracker-ui/src/
- - ./scripts:/home/timetracker/time-tracker-ui/scripts/
- - ./e2e:/home/timetracker/time-tracker-ui/e2e/
- - ./coverage:/home/timetracker/time-tracker-ui/coverage
- - ./angular.json:/home/timetracker/time-tracker-ui/angular.json
- - ./karma.conf.js:/home/timetracker/time-tracker-ui/karma.conf.js
- - ./package.json:/home/timetracker/time-tracker-ui/package.json
- - ./webpack.config.js:/home/timetracker/time-tracker-ui/webpack.config.js
+
diff --git a/e2e/protractor.conf.js b/e2e/protractor.conf.js
index 7c798cfff..17f6b4754 100644
--- a/e2e/protractor.conf.js
+++ b/e2e/protractor.conf.js
@@ -13,7 +13,14 @@ exports.config = {
'./src/**/*.e2e-spec.ts'
],
capabilities: {
- browserName: 'chrome'
+ browserName: 'chrome',
+ 'chromeOptions': {
+ 'args': [
+ '--no-sandbox',
+ '--headless',
+ '--window-size=1024,768'
+ ]
+ }
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
@@ -29,4 +36,4 @@ exports.config = {
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
-};
\ No newline at end of file
+};
diff --git a/karma.conf.js b/karma.conf.js
index c51657f20..afb638d5f 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -1,6 +1,7 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
+
module.exports = function (config) {
config.set({
basePath: '',
@@ -21,6 +22,12 @@ module.exports = function (config) {
seed: '90967',
}, // leave Jasmine Spec Runner output visible in browser
},
+ // Karma Typescript compiler options
+ karmaTypescriptConfig: {
+ coverageOptions : {
+ instrumentation: false
+ }
+ },
coverageIstanbulReporter: {
dir: require('path').join(__dirname, './coverage/time-tracker'),
reports: ['html', 'lcovonly', 'text-summary'],
diff --git a/package.json b/package.json
index fc71525be..38b121695 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,7 @@
"ng": "ng",
"start": "ng serve",
"build": "ng build --prod",
- "test": "ng test",
+ "test": "ng test --browsers ChromeHeadless",
"test-headless": "ng test --browsers ChromeHeadless",
"ci-test": "ng test --no-watch --no-progress --browsers ChromeHeadless",
"lint": "ng lint",
diff --git a/scripts/populate-keys.sh b/scripts/populate-keys.sh
index f395689af..6ab4a5cd4 100644
--- a/scripts/populate-keys.sh
+++ b/scripts/populate-keys.sh
@@ -1,10 +1,10 @@
#!/bin/bash
-> src/environments/keys.ts
-echo 'export const AUTHORITY = "'$AUTHORITY'";' >> src/environments/keys.ts
-echo 'export const CLIENT_ID = "'$CLIENT_ID'";' >> src/environments/keys.ts
-echo 'export const SCOPES = ["'$SCOPES'"];' >> src/environments/keys.ts
-echo 'export const STACK_EXCHANGE_ID = "'$STACK_EXCHANGE_ID'";' >> src/environments/keys.ts
-echo 'export const STACK_EXCHANGE_ACCESS_TOKEN = "'$STACK_EXCHANGE_ACCESS_TOKEN'";' >> src/environments/keys.ts
-echo 'export const AZURE_APP_CONFIGURATION_CONNECTION_STRING = "'$AZURE_APP_CONFIGURATION_CONNECTION_STRING'";' >> src/environments/keys.ts
-cat src/environments/keys.ts
+> .env
+echo 'AUTHORITY = '$AUTHORITY'' >> .env
+echo 'CLIENT_ID = '$CLIENT_ID'' >> .env
+echo 'SCOPES = '$SCOPES'' >> .env
+echo 'STACK_EXCHANGE_ID = '$STACK_EXCHANGE_ID'' >> .env
+echo 'STACK_EXCHANGE_ACCESS_TOKEN = '$STACK_EXCHANGE_ACCESS_TOKEN'' >> .env
+echo 'AZURE_APP_CONFIGURATION_CONNECTION_STRING = '$AZURE_APP_CONFIGURATION_CONNECTION_STRING'' >> .env
+cat .env