Skip to content
Snippets Groups Projects
Unverified Commit 87245fd0 authored by Alexander Samusev's avatar Alexander Samusev Committed by GitHub
Browse files

ci: Move from Gitlab to Github (#1531)

* [ci] Move from Gitlab to Github

* add build docker jobs

* add deploy

* rename env in deploy

* move e2e to pr, rename pr.yml to ci.yml, add benchmark

* fix deply

* artifact for benches

* run benches in github and store results in gh-pages

* fix path

* try benches in my branch

* fix vats

* install git

* 2 steps bench

* rm test from benches

* rm tags from benches

* rm gitlab

* change deps

* upd artifacts action version
parent cef2d10e
Branches
Tags
No related merge requests found
name: Benchmark
on:
push:
branches:
- master
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
benchmark:
name: benchmark
runs-on: ubuntu-latest
environment: master_n_tags
container:
image: paritytech/node-wrk:latest
steps:
- name: Checkout sources
uses: actions/checkout@v4
- name: benchmarks
run: |
yarn --immutable
echo "Benchmarks for polkadot"
mkdir -p artifacts
yarn bench --log-level info --ws-url wss://rpc.polkadot.io
mv benchmarks.txt artifacts/
- name: upload artifacts
uses: actions/upload-artifact@v4
with:
name: benchmarks
path: ./artifacts/
benchmark-publish:
name: benchmark publish
runs-on: ubuntu-latest
needs: [benchmark]
environment: master_n_tags
steps:
- name: Checkout sources
uses: actions/checkout@v4
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ secrets.GH_APP_SAS_APP_ID }}
private-key: ${{ secrets.GH_APP_SAS_APP_KEY }}
- name: download artifacts
uses: actions/download-artifact@v4
with:
name: benchmarks
path: artifacts
- name: Modify benches result for benhcmark action
run: ./scripts/ci/benchmarks/generate_benchmark_result.sh artifacts/benchmarks.txt > artifacts/benchmarks.json
- name: Store benchmark result
uses: benchmark-action/github-action-benchmark@d48d326b4ca9ba73ca0cd0d59f108f9e02a381c7 #1.20.4
with:
tool: "customSmallerIsBetter"
output-file-path: artifacts/benchmarks.json
# Push and deploy GitHub pages branch automatically
auto-push: true
github-token: ${{ steps.app-token.outputs.token }}
...@@ -2,9 +2,13 @@ name: calc ...@@ -2,9 +2,13 @@ name: calc
on: on:
push: push:
branches: [ master ] branches: [master]
pull_request: pull_request:
branches: [ master ] branches: [master]
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs: jobs:
build: build:
......
...@@ -8,6 +8,10 @@ on: ...@@ -8,6 +8,10 @@ on:
branches: branches:
- master - master
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs: jobs:
lint: lint:
# The type of runner that the job will run on # The type of runner that the job will run on
...@@ -21,7 +25,7 @@ jobs: ...@@ -21,7 +25,7 @@ jobs:
- name: Install Node v18 - name: Install Node v18
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: '18.14' node-version: "18.14"
- name: Get yarn cache directory path - name: Get yarn cache directory path
id: yarn-cache-dir-path id: yarn-cache-dir-path
...@@ -41,7 +45,7 @@ jobs: ...@@ -41,7 +45,7 @@ jobs:
- name: Linter. - name: Linter.
run: yarn lint run: yarn lint
tests: tests:
# The type of runner that the job will run on # The type of runner that the job will run on
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
...@@ -51,7 +55,7 @@ jobs: ...@@ -51,7 +55,7 @@ jobs:
- name: Install Node v18 - name: Install Node v18
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: '18.14' node-version: "18.14"
- name: Get yarn cache directory path - name: Get yarn cache directory path
id: yarn-cache-dir-path id: yarn-cache-dir-path
...@@ -82,7 +86,7 @@ jobs: ...@@ -82,7 +86,7 @@ jobs:
- name: Install Node v18 - name: Install Node v18
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: '18.14' node-version: "18.14"
- name: Get yarn cache directory path - name: Get yarn cache directory path
id: yarn-cache-dir-path id: yarn-cache-dir-path
...@@ -113,7 +117,7 @@ jobs: ...@@ -113,7 +117,7 @@ jobs:
- name: Install Node v18 - name: Install Node v18
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: '18.14' node-version: "18.14"
- name: Get yarn cache directory path - name: Get yarn cache directory path
id: yarn-cache-dir-path id: yarn-cache-dir-path
...@@ -134,11 +138,11 @@ jobs: ...@@ -134,11 +138,11 @@ jobs:
run: yarn build:docs run: yarn build:docs
build-npm-release: build-npm-release:
# This test is to make sure sidecar can release a binary without any errors. # This test is to make sure sidecar can release a binary without any errors.
# This script does not publish a release, but instead uses yarn to create a tarball and # This script does not publish a release, but instead uses yarn to create a tarball and
# install it locally. Once installed a binary is attached to sidecars node_modules, and that # install it locally. Once installed a binary is attached to sidecars node_modules, and that
# binary is then tested against. For more in depth information reference the docs at # binary is then tested against. For more in depth information reference the docs at
# `../../scripts/README.md`. # `../../scripts/README.md`.
runs-on: ubuntu-latest runs-on: ubuntu-latest
...@@ -147,10 +151,50 @@ jobs: ...@@ -147,10 +151,50 @@ jobs:
node-version: [18.x] node-version: [18.x]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }} - name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
- run: yarn - run: yarn
- run: yarn test:test-release - run: yarn test:test-release
build_docker:
name: Build docker image
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v4
- name: Build Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: false
tags: |
docker.io/parity/substrate-api-sidecar:latest
e2e:
name: e2e
runs-on: ubuntu-latest
container:
image: node:18
strategy:
matrix:
include:
- chain-name: westend
chain-url: wss://westend-rpc.polkadot.io
- chain-name: kusama
chain-url: wss://apps-kusama-rpc.polkadot.io
- chain-name: polkadot
chain-url: wss://apps-rpc.polkadot.io
steps:
- name: Checkout sources
uses: actions/checkout@v4
- name: e2e tests
run: |
yarn --immutable
echo "Tests for ${{ matrix.chain-name }}"
yarn test:latest-e2e-tests --log-level info --chain ${{ matrix.chain-name }} --local ${{ matrix.chain-url }}
name: Deploy
on:
push:
branches:
- master
tags:
- v*
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
#to use reusable workflow
permissions:
id-token: write
contents: read
env:
APP: "substrate-api-sidecar"
jobs:
set-variables:
# This workaround sets the container image for each job using 'set-variables' job output.
# env variables don't work for PR from forks, so we need to use outputs.
runs-on: ubuntu-latest
outputs:
VERSION: ${{ steps.version.outputs.VERSION }}
steps:
- name: Define version
id: version
run: |
export COMMIT_SHA=${{ github.sha }}
export COMMIT_SHA_SHORT=${COMMIT_SHA:0:8}
export REF_NAME=${{ github.ref_name }}
export REF_SLUG=${REF_NAME//\//_}
if [[ ${REF_SLUG} == "master" ]]
then
VERSION=${REF_SLUG}-${COMMIT_SHA_SHORT}
echo "VERSION=${REF_SLUG}-${COMMIT_SHA_SHORT}" >> $GITHUB_OUTPUT
else
VERSION=${REF_SLUG}
echo "VERSION=${REF_SLUG}" >> $GITHUB_OUTPUT
fi
echo "set VERSION=${VERSION}"
build_push_docker:
name: Build docker image
runs-on: ubuntu-latest
environment: master_n_tags
needs: [set-variables]
env:
VERSION: ${{ needs.set-variables.outputs.VERSION }}
steps:
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Build Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: |
docker.io/parity/substrate-api-sidecar:${{ env.VERSION }}
deploy-stg-kusama:
name: Deploy Staging
runs-on: ubuntu-latest
needs: [set-variables, build_push_docker]
environment: parity-stg
env:
VERSION: ${{ needs.set-variables.outputs.VERSION }}
ARGOCD_SERVER: "argocd-stg.teleport.parity.io"
steps:
- name: Deploy to ArgoCD
uses: paritytech/argocd-deployment-action@main
with:
environment: "parity-stg"
tag: "${{ env.VERSION }}"
app_name: "substrate-api-sidecar-kusama"
app_packages: "${{ env.APP }}"
argocd_server: ${{ env.ARGOCD_SERVER }}
teleport_token: ${{ env.APP }}
teleport_app_name: "argocd-stg"
argocd_auth_token: ${{ secrets.ARGOCD_AUTH_TOKEN }}
deploy-stg-polkadot:
name: Deploy Staging
runs-on: ubuntu-latest
needs: [set-variables, build_push_docker]
environment: parity-stg
env:
VERSION: ${{ needs.set-variables.outputs.VERSION }}
ARGOCD_SERVER: "argocd-stg.teleport.parity.io"
steps:
- name: Deploy to ArgoCD
uses: paritytech/argocd-deployment-action@main
with:
environment: "parity-stg"
tag: "${{ env.VERSION }}"
app_name: "substrate-api-sidecar-polkadot"
app_packages: "${{ env.APP }}"
argocd_server: ${{ env.ARGOCD_SERVER }}
teleport_token: ${{ env.APP }}
teleport_app_name: "argocd-stg"
argocd_auth_token: ${{ secrets.ARGOCD_AUTH_TOKEN }}
deploy-prod-kusama:
name: Deploy Production
runs-on: ubuntu-latest
needs: [set-variables, deploy-stg-kusama]
# Deploy only if the tag is v*
if: startsWith(github.ref, 'refs/tags/v')
environment: parity-chains
env:
VERSION: ${{ needs.set-variables.outputs.VERSION }}
ARGOCD_SERVER: "argocd-chains.teleport.parity.io"
steps:
- name: Deploy to ArgoCD
uses: paritytech/argocd-deployment-action@main
with:
environment: "parity-chains"
tag: "${{ env.VERSION }}"
app_name: "substrate-api-sidecar-kusama"
app_packages: "${{ env.APP }}"
argocd_server: ${{ env.ARGOCD_SERVER }}
teleport_token: ${{ env.APP }}
teleport_app_name: "argocd-chains"
argocd_auth_token: ${{ secrets.ARGOCD_AUTH_TOKEN }}
deploy-prod-polkadot:
name: Deploy Production
runs-on: ubuntu-latest
needs: [set-variables, deploy-stg-polkadot]
# Deploy only if the tag is v*
if: startsWith(github.ref, 'refs/tags/v')
environment: parity-chains
env:
VERSION: ${{ needs.set-variables.outputs.VERSION }}
ARGOCD_SERVER: "argocd-chains.teleport.parity.io"
steps:
- name: Deploy to ArgoCD
uses: paritytech/argocd-deployment-action@main
with:
environment: "parity-chains"
tag: "${{ env.VERSION }}"
app_name: "substrate-api-sidecar-polkadot"
app_packages: "${{ env.APP }}"
argocd_server: ${{ env.ARGOCD_SERVER }}
teleport_token: ${{ env.APP }}
teleport_app_name: "argocd-chains"
argocd_auth_token: ${{ secrets.ARGOCD_AUTH_TOKEN }}
name: gitspiegel sync
# This workflow doesn't do anything, it's only use is to trigger "workflow_run"
# webhook, that'll be consumed by gitspiegel
# This way, gitspiegel won't do mirroring, unless this workflow runs,
# and running the workflow is protected by GitHub
on:
pull_request:
types:
- opened
- synchronize
- unlocked
- ready_for_review
- reopened
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Do nothing
run: echo "let's go"
...@@ -13,11 +13,15 @@ on: ...@@ -13,11 +13,15 @@ on:
- opened - opened
- edited - edited
- synchronize - synchronize
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs: jobs:
validate-title: validate-title:
permissions: permissions:
pull-requests: read # for amannn/action-semantic-pull-request to analyze PRs pull-requests: read # for amannn/action-semantic-pull-request to analyze PRs
name: Validate PR Title name: Validate PR Title
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
......
# Gitlab-CI Workflow
# stages:
# build:
# - Runs on commits on master or tags that match the pattern v1.0, v2.1rc1
# deploy-staging:
# - Runs on commits on master or tags that match the pattern v1.0, v2.1rc1 (continues deployment)
# deploy-production:
# - Runs on tags that match the pattern v1.0, v2.1rc1 (manual deployment)
variables:
CONTAINER_REPO: "docker.io/parity/substrate-api-sidecar"
DOCKERFILE_DIRECTORY: "./"
CI_IMAGE: "$BUILDAH_IMAGE" # defined in group variables
BUILDAH_COMMAND: "buildah --storage-driver overlay2"
NODE_IMAGE: "node:18"
BENCHMARK_IMAGE: "paritytech/node-wrk:latest"
default:
retry:
max: 2
when:
- runner_system_failure
- unknown_failure
- api_failure
interruptible: true
before_script:
- |-
if [[ $CI_COMMIT_TAG =~ ^v[0-9]+\.[0-9]+.*$ ]]; then
export DOCKER_IMAGE_TAG="${CI_COMMIT_TAG}"
export BUILD_LATEST_IMAGE="true"
else
export DOCKER_IMAGE_TAG="${CI_COMMIT_SHORT_SHA}-beta"
fi
stages:
- test
- build
- deploy-staging
- deploy-production
- benchmark
- check-benchmark
- push-benchmark
.collect-artifacts: &collect-artifacts
artifacts:
name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}"
when: on_success
expire_in: 1 days
paths:
- ./artifacts/
.test-refs: &test-refs
rules:
- if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs
.test-refs-manual: &test-refs-manual
rules:
- if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs
when: manual
.publish-refs: &publish-refs
rules:
- if: $CI_PIPELINE_SOURCE == "pipeline"
when: never
- if: $CI_PIPELINE_SOURCE == "schedule"
- if: $CI_COMMIT_REF_NAME == "master"
- if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1
.dockerize: &dockerize
stage: build
image: $CI_IMAGE
tags:
- kubernetes-parity-build
.kubernetes-env: &kubernetes-env
image: $CI_IMAGE
tags:
- kubernetes-parity-build
.deploy-k8s: &deploy-k8s
image: quay.io/argoproj/argocd:v2.7.9
variables:
ARGOCD_OPTS: --grpc-web --grpc-web-root-path /$DOMAIN
script:
- argocd app list
# app1
- argocd app set $APP1 --helm-set substrate-api-sidecar.image.tag="${DOCKER_IMAGE_TAG}"
- argocd app sync $APP1
- argocd app wait $APP1 --timeout 180
# app2
- argocd app set $APP2 --helm-set substrate-api-sidecar.image.tag="${DOCKER_IMAGE_TAG}"
- argocd app sync $APP2
- argocd app wait $APP2 --timeout 180
tags:
- kubernetes-parity-build
.e2e-template: &e2e-template
stage: test
<<: *kubernetes-env
variables:
CI_IMAGE: $NODE_IMAGE
CHAIN_NAME: ""
CHAIN_URL: ""
script:
- yarn --immutable
- echo "Tests for ${CHAIN_URL}"
- yarn test:latest-e2e-tests --log-level info --chain ${CHAIN_NAME} --local ${CHAIN_URL}
allow_failure: true
e2e-westend:
<<: *e2e-template
variables:
CI_IMAGE: $NODE_IMAGE
CHAIN_NAME: "westend"
CHAIN_URL: "wss://westend-rpc.polkadot.io"
e2e-kusama:
<<: *e2e-template
variables:
CI_IMAGE: $NODE_IMAGE
CHAIN_NAME: "kusama"
CHAIN_URL: "wss://apps-kusama-rpc.polkadot.io"
e2e-polkadot:
<<: *e2e-template
variables:
CI_IMAGE: $NODE_IMAGE
CHAIN_NAME: "polkadot"
CHAIN_URL: "wss://apps-rpc.polkadot.io"
build-docker:
<<: *dockerize
<<: *publish-refs
script:
- echo building "$CONTAINER_REPO:$DOCKER_IMAGE_TAG"
- if [[ $BUILD_LATEST_IMAGE ]]; then
$BUILDAH_COMMAND build
--format=docker
--tag "$CONTAINER_REPO:$DOCKER_IMAGE_TAG"
--tag "$CONTAINER_REPO:latest" "$DOCKERFILE_DIRECTORY";
else
$BUILDAH_COMMAND build
--format=docker
--tag "$CONTAINER_REPO:$DOCKER_IMAGE_TAG" "$DOCKERFILE_DIRECTORY";
fi
- $BUILDAH_COMMAND info
- echo ${Docker_Hub_Pass_Parity} |
buildah login --username ${Docker_Hub_User_Parity} --password-stdin docker.io
- echo pushing "$CONTAINER_REPO:$DOCKER_IMAGE_TAG"
- if [[ $BUILD_LATEST_IMAGE ]]; then
$BUILDAH_COMMAND push --format=v2s2 "$CONTAINER_REPO:$DOCKER_IMAGE_TAG";
$BUILDAH_COMMAND push --format=v2s2 "$CONTAINER_REPO:latest";
else
$BUILDAH_COMMAND push --format=v2s2 "$CONTAINER_REPO:$DOCKER_IMAGE_TAG";
fi
push-docker-image-description:
stage: build
before_script:
- echo
extends:
- .kubernetes-env
variables:
CI_IMAGE: paritytech/dockerhub-description
DOCKERHUB_REPOSITORY: parity/substrate-api-sidecar
DOCKER_USERNAME: $Docker_Hub_User_Parity
DOCKER_PASSWORD: $Docker_Hub_Pass_Parity
README_FILEPATH: $CI_PROJECT_DIR/Dockerfile.README.md
SHORT_DESCRIPTION: "REST service to interact with blockchain nodes built using Substrate's FRAME framework."
rules:
- if: $CI_COMMIT_REF_NAME == "master"
changes:
- Dockerfile.README.md
script:
- cd / && sh entrypoint.sh
# checks that dockerimage can be built without publishing
build-docker-pr:
<<: *dockerize
<<: *test-refs
script:
- echo building "$CONTAINER_REPO:$DOCKER_IMAGE_TAG"
- if [[ $BUILD_LATEST_IMAGE ]]; then
$BUILDAH_COMMAND build
--format=docker
--tag "$CONTAINER_REPO:$DOCKER_IMAGE_TAG"
--tag "$CONTAINER_REPO:latest" "$DOCKERFILE_DIRECTORY";
else
$BUILDAH_COMMAND build
--format=docker
--tag "$CONTAINER_REPO:$DOCKER_IMAGE_TAG" "$DOCKERFILE_DIRECTORY";
fi
- $BUILDAH_COMMAND info
deploy-staging:
stage: deploy-staging
extends: .deploy-k8s
<<: *publish-refs
environment:
name: parity-stg
variables:
DOMAIN: parity-stg
APP1: substrate-api-sidecar-kusama
APP2: substrate-api-sidecar-polkadot
deploy-production:
stage: deploy-production
extends: .deploy-k8s
environment:
name: parity-chains
variables:
DOMAIN: parity-chains
APP1: substrate-api-sidecar-kusama
APP2: substrate-api-sidecar-polkadot
rules:
- if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1
when: manual
bench-polkadot: &bench-polkadot
stage: benchmark
<<: *kubernetes-env
<<: *collect-artifacts
<<: *publish-refs
variables:
CI_IMAGE: $BENCHMARK_IMAGE
CHAIN_NAME: "polkadot"
CHAIN_URL: "wss://rpc.polkadot.io"
script:
- yarn --immutable
- echo "Benchmarks for ${CHAIN_URL}"
- mkdir -p artifacts
- yarn bench --log-level info --ws-url ${CHAIN_URL}
- mv benchmarks.txt artifacts/
# manual step to run benchmarks in PR pipeline
bench-polkadot-manual-pr:
stage: benchmark
<<: *bench-polkadot
<<: *test-refs-manual
# temporary disabled for collecting results
# check-benchmark:
# stage: check-benchmark
# <<: *publish-refs
# <<: *kubernetes-env
# <<: *collect-artifacts
# needs:
# - job: benchmark
# artifacts: true
# variables:
# GITHUB_REPO: "paritytech/substrate-api-sidecar"
# CI_IMAGE: "paritytech/benchmarks:latest"
# THRESHOLD: 60000
# GITHUB_TOKEN: $GITHUB_PR_TOKEN
# script:
# - export RESULT=$(cat artifacts/result.txt | grep AvgRequestTime | awk '{print $2}')
# - check_single_bench_result -g $GITHUB_REPO
# -c $THRESHOLD
# -v $RESULT
push-benchmark:
stage: push-benchmark
<<: *publish-refs
<<: *kubernetes-env
needs:
- job: bench-polkadot
artifacts: true
variables:
PROMETHEUS_URL: "https://pushgateway.parity-build.parity.io"
CI_IMAGE: "paritytech/benchmarks:latest"
script:
- scripts/ci/benchmarks/push_benchmark_results.sh artifacts/benchmarks.txt
#!/bin/bash
# The script parses benchmarks.txt file and creates file suitable for https://github.com/benchmark-action/github-action-benchmark
# Usage: generate_benchmark_result.sh <path_to_benchmarks.txt>
RESULT_FILE=$1
if [ -z "${RESULT_FILE}" ]
then
echo "Usage: $0 <path_to_benchmarks.txt>"
exit 1
fi
BENCHMARKS=$(cat ${RESULT_FILE} | grep Result | awk '{print $3}')
NBENCHMARKS=$(echo ${BENCHMARKS} | wc -w)
COUNTER=0
echo "["
for benchmark in ${BENCHMARKS}
do
COUNTER=$((COUNTER+1))
# "/accounts/{accountId}/balance-info:" -> "accounts-{accountId}-balance-info"
benchmark_name=$(echo ${benchmark} | cut -d ":" -f1 | sed 's/\///' | sed 's/\//-/g' )
result=$(cat ${RESULT_FILE} | grep -A 20 ${benchmark} | grep "Avg RequestTime(Latency)" | awk '{print $3}')
unit=${result: -2}
result_value=${result::-2}
echo " {"
echo " \"name\": \"${benchmark_name}\","
echo " \"result\": ${result_value},"
echo " \"unit\": \"${unit}\""
if [ $COUNTER -eq $NBENCHMARKS ]
then
echo " }"
else
echo " },"
fi
done
echo "]"
\ No newline at end of file
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment