diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..253bcb76 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: daily diff --git a/.github/scripts/get-latest-tag.sh b/.github/scripts/get-latest-tag.sh new file mode 100755 index 00000000..1ce4bc38 --- /dev/null +++ b/.github/scripts/get-latest-tag.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +TAGS=$(git ls-remote --refs --tags --sort='v:refname' https://github.com/$REPO "v$VERSION.*") +TAG=$(echo $TAGS | tail -n1 | sed 's/.*\///') +echo $TAG diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 00000000..8f7ba487 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,193 @@ +name: Build + +on: + push: + # branches: + # - main + # paths: + # - github/workflows/docker.yml + # - build/** + # - installation/** + # - tests/** + # - .dockerignore + # - docker-bake.hcl + # - docker-compose.yml + # - env* + + pull_request: + branches: + - main + paths: + - github/workflows/docker.yml + - build/** + - installation/** + - tests/** + - .dockerignore + - docker-bake.hcl + - docker-compose.yml + - env* + + # Nightly builds at 12:00 am + schedule: + - cron: 0 0 * * * + + repository_dispatch: # Triggered from frappe/frappe and frappe/erpnext on releases + + workflow_dispatch: # Manually triggered + inputs: + version: + description: Frappe and ERPNext version. Set to "12", "13" for latest stable versions or "develop" for nightly builds. + required: true + +jobs: + resolve-matrix: + name: Resolve matrix configuration + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.get-matrix.outputs.matrix }} + build-target: ${{ steps.get-build-target.outputs.build-target }} + + steps: + - uses: actions/checkout@v2 + + - name: Get matrix + id: get-matrix + run: | + if [ $GITHUB_EVENT_NAME == "repository_dispatch" ] || [ $GITHUB_EVENT_NAME == "push" ]; then + MATRIX='[{"version": "12"}, {"version": "13"}]' + elif [ $GITHUB_EVENT_NAME == "schedule" ] || [ $GITHUB_EVENT_NAME == "pull_request" ]; then + MATRIX='[{"version": "develop"}]' + elif [ $GITHUB_EVENT_NAME == "workflow_dispatch" ]; then + MATRIX='[{"version": "${{ github.event.inputs.version }}"}]' + fi + + echo ::set-output name=matrix::{\"include\":$MATRIX} + echo $MATRIX + + - name: Get build target + id: get-build-target + run: | + IS_DEVELOP=$(echo $MATRIX | jq 'any(.include[].version == "develop"; .)') + if [ $IS_DEVELOP == "true" ]; then + BUILD_TARGET_SUFFIX="develop" + else + BUILD_TARGET_SUFFIX="stable" + fi + echo $BUILD_TARGET_SUFFIX + echo ::set-output name=build-target::$BUILD_TARGET_SUFFIX + env: + MATRIX: ${{ steps.get-matrix.outputs.matrix }} + + build_bench: + name: Bench image + needs: resolve-matrix + runs-on: ubuntu-latest + if: needs.resolve-matrix.outputs.build-target == 'develop' + + steps: + - uses: actions/checkout@v2 + - uses: docker/setup-buildx-action@v1 + # - uses: docker/login-action@v1 + # with: + # username: ${{ secrets.DOCKERHUB_USERNAME }} + # password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push + uses: docker/bake-action@v1.6.0 + with: + files: docker-bake.hcl + targets: frappe-bench + # push: ${{ github.event_name != 'pull_request' }} + + build_main: + name: Frappe and ERPNext images + runs-on: ubuntu-latest + strategy: + matrix: ${{ fromJson(needs.resolve-matrix.outputs.matrix) }} + needs: resolve-matrix + + steps: + - uses: actions/checkout@v2 + - uses: docker/setup-buildx-action@v1 + # - uses: docker/login-action@v1 + # with: + # username: ${{ secrets.DOCKERHUB_USERNAME }} + # password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Get latest Frappe tag + if: needs.resolve-matrix.outputs.build-target == 'stable' + run: | + GIT_TAG=$(./.github/scripts/get-latest-tag.sh) + echo $GIT_TAG + echo "GIT_TAG=$GIT_TAG" >> $GITHUB_ENV + echo "GIT_BRANCH=version-$VERSION" >> $GITHUB_ENV + echo "VERSION=$VERSION" >> $GITHUB_ENV + env: + REPO: frappe/frappe + VERSION: ${{ matrix.version }} + + - name: Build Frappe images + uses: docker/bake-action@v1.6.0 + with: + files: docker-bake.hcl + targets: ${{ format('{0}-{1}', 'frappe', needs.resolve-matrix.outputs.build-target )}} + load: true + + - name: Get latest ERPNext tag + if: needs.resolve-matrix.outputs.build-target == 'stable' + run: | + GIT_TAG=$(./.github/scripts/get-latest-tag.sh) + echo $GIT_TAG + echo "GIT_TAG=$GIT_TAG" >> $GITHUB_ENV + echo "GIT_BRANCH=version-$VERSION" >> $GITHUB_ENV + echo "VERSION=$VERSION" >> $GITHUB_ENV + env: + REPO: frappe/erpnext + VERSION: ${{ matrix.version }} + + - name: Build ERPNext images + uses: docker/bake-action@v1.6.0 + with: + files: docker-bake.hcl + targets: ${{ format('{0}-{1}', 'erpnext', needs.resolve-matrix.outputs.build-target )}} + load: true + + - name: Install test dependencies + run: sudo apt-get install -y w3m + + - name: Test + run: | + ./tests/check-format.sh + ./tests/docker-test.sh + # This is done to not to rebuild images in the next step + git reset --hard @{u} + + - name: Push Frappe images + if: github.event_name != 'pull_request' + uses: docker/bake-action@v1.6.0 + with: + files: docker-bake.hcl + targets: ${{ format('{0}-{1}', 'frappe', needs.resolve-matrix.outputs.build-target )}} + # push: true + + - name: Push ERPNext images + if: github.event_name != 'pull_request' + uses: docker/bake-action@v1.6.0 + with: + files: docker-bake.hcl + targets: ${{ format('{0}-{1}', 'erpnext', needs.resolve-matrix.outputs.build-target )}} + # push: true + + - name: Release Helm Chart + if: needs.resolve-matrix.outputs.build-target == 'stable' + run: | + export GIT_SSH_COMMAND="ssh -i ${PWD}/deploy_key" + echo "$HELM_DEPLOY_KEY" | openssl aes-256-cbc -K $ENCRYPTED_KEY -iv $ENCRYPTED_IV -out deploy_key -d + chmod 400 deploy_key; + ssh-keyscan github.com >> $HOME/.ssh/known_hosts 2>/dev/null; + pip install --upgrade pip + git clone git@github.com:frappe/helm.git && cd helm + pip install -r release_wizard/requirements.txt + ./release_wizard/wizard 13 patch --remote origin --ci + env: + HELM_DEPLOY_KEY: ${{ secrets.HELM_DEPLOY_KEY }} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6d6f8fcb..00000000 --- a/.travis.yml +++ /dev/null @@ -1,187 +0,0 @@ -sudo: required - -dist: bionic - -language: python - -python: - - '3.6' - -services: - - docker - -before_install: - - if [[ "$BUILD" != "development" ]]; then - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin; - sudo apt-get update && sudo apt-get -y install git; - fi - - if [[ $BUILD == "development" ]];then - sudo apt-get update && sudo apt-get -y install docker-compose; - fi - - chmod u+x ./travis.py - -after_success: - - docker --version - -jobs: - include: - - stage: "Build Frappe bench development environment (latest)" - if: branch = develop AND type != pull_request - script: - - docker build -t frappe/bench:latest -f build/bench/Dockerfile . - - docker push frappe/bench:latest - - stage: "Frappe (edge)" - if: branch = develop AND type != pull_request - script: - - ./travis.py frappe --worker --tag latest - - ./travis.py frappe --worker --tag edge --tag-only - - ./travis.py frappe --worker --tag develop --tag-only - - stage: "Frappe (edge)" - if: branch = develop AND type != pull_request - script: - - ./travis.py frappe --nginx --tag latest - - ./travis.py frappe --nginx --tag edge --tag-only - - ./travis.py frappe --nginx --tag develop --tag-only - - stage: "Frappe (edge)" - if: branch = develop AND type != pull_request - script: - - ./travis.py frappe --socketio --tag latest - - ./travis.py frappe --socketio --tag edge --tag-only - - ./travis.py frappe --socketio --tag develop --tag-only - - stage: "ERPNext (edge)" - if: branch = develop AND type != pull_request - script: - - ./travis.py erpnext --worker --tag latest - - ./travis.py erpnext --worker --tag edge --tag-only - - ./travis.py erpnext --worker --tag develop --tag-only - - stage: "ERPNext (edge)" - if: branch = develop AND type != pull_request - script: - - ./travis.py erpnext --nginx --tag latest - - ./travis.py erpnext --nginx --tag edge --tag-only - - ./travis.py erpnext --nginx --tag develop --tag-only - - stage: "Frappe (v13)" - if: branch = master AND type != pull_request - script: - - ./travis.py frappe --worker --git-version 13 - - ./travis.py frappe --worker --tag v13 --tag-only - - ./travis.py frappe --worker --tag version-13 --tag-only - - stage: "Frappe (v13)" - if: branch = master AND type != pull_request - script: - - ./travis.py frappe --nginx --git-version 13 - - ./travis.py frappe --nginx --tag v13 --tag-only - - ./travis.py frappe --nginx --tag version-13 --tag-only - - stage: "Frappe (v13)" - if: branch = master AND type != pull_request - script: - - ./travis.py frappe --socketio --git-version 13 - - ./travis.py frappe --socketio --tag v13 --tag-only - - ./travis.py frappe --socketio --tag version-13 --tag-only - - stage: "ERPNext (v13)" - if: branch = master AND type != pull_request - script: - - ./travis.py erpnext --worker --git-version 13 - - ./travis.py erpnext --worker --tag v13 --tag-only - - ./travis.py erpnext --worker --tag version-13 --tag-only - - stage: "ERPNext (v13)" - if: branch = master AND type != pull_request - script: - - ./travis.py erpnext --nginx --git-version 13 - - ./travis.py erpnext --nginx --tag v13 --tag-only - - ./travis.py erpnext --nginx --tag version-13 --tag-only - - stage: "Frappe (v12)" - if: branch = master AND type != pull_request - script: - - ./travis.py frappe --worker --git-version 12 - - ./travis.py frappe --worker --tag v12 --tag-only - - ./travis.py frappe --worker --tag version-12 --tag-only - - stage: "Frappe (v12)" - if: branch = master AND type != pull_request - script: - - ./travis.py frappe --nginx --git-version 12 - - ./travis.py frappe --nginx --tag v12 --tag-only - - ./travis.py frappe --nginx --tag version-12 --tag-only - - stage: "Frappe (v12)" - if: branch = master AND type != pull_request - script: - - ./travis.py frappe --socketio --git-version 12 - - ./travis.py frappe --socketio --tag v12 --tag-only - - ./travis.py frappe --socketio --tag version-12 --tag-only - - stage: "ERPNext (v12)" - if: branch = master AND type != pull_request - script: - - ./travis.py erpnext --worker --git-version 12 - - ./travis.py erpnext --worker --tag v12 --tag-only - - ./travis.py erpnext --worker --tag version-12 --tag-only - - stage: "ERPNext (v12)" - if: branch = master AND type != pull_request - script: - - ./travis.py erpnext --nginx --git-version 12 - - ./travis.py erpnext --nginx --tag v12 --tag-only - - ./travis.py erpnext --nginx --tag version-12 --tag-only - - stage: "Frappe (v11)" - if: branch = master AND type != pull_request - script: - - ./travis.py frappe --worker --git-version 11 - - ./travis.py frappe --worker --tag v11 --tag-only - - ./travis.py frappe --worker --tag version-11 --tag-only - - stage: "Frappe (v11)" - if: branch = master AND type != pull_request - script: - - ./travis.py frappe --nginx --git-version 11 - - ./travis.py frappe --nginx --tag v11 --tag-only - - ./travis.py frappe --nginx --tag version-11 --tag-only - - stage: "Frappe (v11)" - if: branch = master AND type != pull_request - script: - - ./travis.py frappe --socketio --git-version 11 - - ./travis.py frappe --socketio --tag v11 --tag-only - - ./travis.py frappe --socketio --tag version-11 --tag-only - - stage: "ERPNext (v11)" - if: branch = master AND type != pull_request - script: - - ./travis.py erpnext --worker --git-version 11 - - ./travis.py erpnext --worker --tag v11 --tag-only - - ./travis.py erpnext --worker --tag version-11 --tag-only - - stage: "ERPNext (v11)" - if: branch = master AND type != pull_request - script: - - ./travis.py erpnext --nginx --git-version 11 - - ./travis.py erpnext --nginx --tag v11 --tag-only - - ./travis.py erpnext --nginx --tag version-11 --tag-only - - stage: "Build and test edge images" - if: type = pull_request - before_install: - - sudo curl -L "https://github.com/docker/compose/releases/download/1.26.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose - - sudo chmod +x /usr/local/bin/docker-compose - - sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose - - sudo apt-get update && sudo apt-get -y install w3m shellcheck - script: - - ./tests/check-format.sh - - docker build -t frappe/frappe-socketio:edge -f build/frappe-socketio/Dockerfile . - - docker build -t frappe/frappe-worker:develop -f build/frappe-worker/Dockerfile . - - docker build -t frappe/erpnext-worker:edge -f build/erpnext-worker/Dockerfile . - - docker build -t frappe/frappe-nginx:develop -f build/frappe-nginx/Dockerfile . - - docker build -t frappe/erpnext-nginx:edge -f build/erpnext-nginx/Dockerfile . - - ./tests/docker-test.sh - - stage: "Pull and test edge images" - if: branch = develop AND type != pull_request - before_install: - - sudo apt-get update && sudo apt-get -y install docker-compose w3m - script: - - ./tests/docker-test.sh - - stage: "Helm Chart Release" - if: branch = master AND type != pull_request - env: - - GIT_SSH_COMMAND="ssh -i ${TRAVIS_BUILD_DIR}/deploy_key" - before_install: - - openssl aes-256-cbc -K $encrypted_189e52c2c347_key -iv $encrypted_189e52c2c347_iv -in deploy_key.enc -out deploy_key -d; - chmod 400 deploy_key; - - ssh-keyscan github.com >> $HOME/.ssh/known_hosts 2>/dev/null; - install: - - pip install --upgrade pip - script: - - git clone git@github.com:frappe/helm.git && cd helm - - pip install -r release_wizard/requirements.txt - - ./release_wizard/wizard 13 patch --remote origin --ci diff --git a/README.md b/README.md index f66784fe..316747f4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -| Develop | [![Build Status](https://travis-ci.com/frappe/frappe_docker.svg?branch=develop)](https://travis-ci.com/frappe/frappe_docker) | +| Stable | [![stable](https://github.com/frappe/frappe_docker/actions/workflows/docker.yml/badge.svg?event=repository_dispatch)](https://github.com/frappe/frappe_docker/actions/workflows/docker.yml) | |---------|-----------------------------------------------------------------------------------------------------------------------------| -| Master | [![Build Status](https://travis-ci.com/frappe/frappe_docker.svg?branch=master)](https://travis-ci.com/frappe/frappe_docker) | +| Nightly | [![develop](https://github.com/frappe/frappe_docker/actions/workflows/docker.yml/badge.svg?event=schedule)](https://github.com/frappe/frappe_docker/actions/workflows/docker.yml) | ## Getting Started diff --git a/docker-bake.hcl b/docker-bake.hcl new file mode 100644 index 00000000..4de80173 --- /dev/null +++ b/docker-bake.hcl @@ -0,0 +1,129 @@ +# Images + +target "frappe-bench" { + tags = ["frappe/bench:latest"] + dockerfile = "build/bench/Dockerfile" +} + +target "frappe-nginx" { + dockerfile = "build/frappe-nginx/Dockerfile" +} + +target "frappe-worker" { + dockerfile = "build/frappe-worker/Dockerfile" +} + +target "frappe-socketio" { + dockerfile = "build/frappe-socketio/Dockerfile" +} + +target "erpnext-nginx" { + dockerfile = "build/erpnext-nginx/Dockerfile" +} + +target "erpnext-worker" { + dockerfile = "build/erpnext-worker/Dockerfile" +} + + +# Helpers + +target "develop-args" { + args = { + GIT_BRANCH = "develop" + IMAGE_TAG = "develop" + } +} + +variable "GIT_TAG" {} +variable "GIT_BRANCH" {} +variable "VERSION" {} + +target "stable-args" { + args = { + GIT_BRANCH = "${GIT_BRANCH}" + IMAGE_TAG = "${GIT_BRANCH}" + } +} + +function "set_stable_tags" { + params = [repo] + result = ["${repo}:${GIT_TAG}", "${repo}:v${VERSION}", "${repo}:${GIT_BRANCH}"] +} + +function "set_develop_tags" { + params = [repo] + result = ["${repo}:latest", "${repo}:edge", "${repo}:develop"] +} + + +# Develop + +target "frappe-nginx-develop" { + inherits = ["frappe-nginx", "develop-args"] + tags = set_develop_tags("frappe/frappe-nginx") +} + +target "frappe-worker-develop" { + inherits = ["frappe-worker", "develop-args"] + tags = set_develop_tags("frappe/frappe-worker") +} + +target "frappe-socketio-develop" { + inherits = ["frappe-socketio", "develop-args"] + tags = set_develop_tags("frappe/frappe-socketio") +} + +target "erpnext-nginx-develop" { + inherits = ["erpnext-nginx", "develop-args"] + tags = set_develop_tags("frappe/erpnext-nginx") +} + +target "erpnext-worker-develop" { + inherits = ["erpnext-worker", "develop-args"] + tags = set_develop_tags("frappe/erpnext-worker") +} + +group "frappe-develop" { + targets = ["frappe-nginx-develop", "frappe-worker-develop", "frappe-socketio-develop"] +} + +group "erpnext-develop" { + targets = ["erpnext-nginx-develop", "erpnext-worker-develop"] +} + + +# Stable + +target "frappe-nginx-stable" { + inherits = ["frappe-nginx", "stable-args"] + tags = set_stable_tags("frappe/frappe-nginx") +} + +target "frappe-worker-stable" { + inherits = ["frappe-worker", "stable-args"] + tags = set_stable_tags("frappe/frappe-worker") +} + +target "frappe-socketio-stable" { + inherits = ["frappe-socketio", "stable-args"] + tags = set_stable_tags("frappe/frappe-socketio") +} + +target "erpnext-nginx-stable" { + inherits = ["erpnext-nginx", "stable-args"] + tags = set_stable_tags("frappe/erpnext-nginx") +} + +target "erpnext-worker-stable" { + inherits = ["erpnext-worker", "stable-args"] + tags = set_stable_tags("frappe/erpnext-worker") +} + +group "frappe-stable" { + targets = ["frappe-nginx-stable", "frappe-worker-stable", "frappe-socketio-stable"] +} + +group "erpnext-stable" { + targets = ["erpnext-nginx-stable", "erpnext-worker-stable"] +} diff --git a/tests/docker-test.sh b/tests/docker-test.sh index 5b917f21..7693111e 100755 --- a/tests/docker-test.sh +++ b/tests/docker-test.sh @@ -49,9 +49,11 @@ function loopHealthCheck() { echo -e "${ULINE}Copy env-example file${ENDULINE}" cp env-example .env +export $(cat .env) echo -e "${NEWLINE}${ULINE}Set version to v13${ENDULINE}" sed -i -e "s/edge/v13/g" .env +export $(cat .env) echo -e "${NEWLINE}${ULINE}Start Services${ENDULINE}" docker-compose \ @@ -92,6 +94,7 @@ curl -s http://test.localhost | w3m -T text/html -dump echo -e "${NEWLINE}${ULINE}Set version to edge${ENDULINE}" sed -i -e "s/v13/edge/g" .env +export $(cat .env) echo -e "${NEWLINE}${ULINE}Restart containers with edge image${ENDULINE}" docker-compose \ diff --git a/travis.py b/travis.py deleted file mode 100755 index bff2d3b4..00000000 --- a/travis.py +++ /dev/null @@ -1,150 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import subprocess -import os - - -def parse_args(): - parser = argparse.ArgumentParser( - description="frappe_docker common CI elements", add_help=True - ) - - parser.add_argument( - "service", - action="store", - type=str, - help='Name of the service to build: "erpnext" or "frappe"', - ) - parser.add_argument( - "-o", - "--tag-only", - required=False, - action="store_true", - dest="tag_only", - help="Only tag an image and push it.", - ) - parser.add_argument( - "-b", - "--is-beta", - required=False, - default=False, - action="store_true", - dest="is_beta", - help="Specify if tag is beta", - ) - - image_type = parser.add_mutually_exclusive_group(required=True) - image_type.add_argument( - "-a", - "--nginx", - action="store_const", - dest="image_type", - const="nginx", - help="Build the nginx + static assets image", - ) - image_type.add_argument( - "-s", - "--socketio", - action="store_const", - dest="image_type", - const="socketio", - help="Build the frappe-socketio image", - ) - image_type.add_argument( - "-w", - "--worker", - action="store_const", - dest="image_type", - const="worker", - help="Build the python environment image", - ) - - tag_type = parser.add_mutually_exclusive_group(required=True) - tag_type.add_argument( - "-g", - "--git-version", - action="store", - type=str, - dest="version", - help='The version number of service (i.e. "11", "12", etc.)', - ) - tag_type.add_argument( - "-t", - "--tag", - action="store", - type=str, - dest="tag", - help="The image tag (i.e. erpnext-worker:$TAG )", - ) - - args = parser.parse_args() - return args - - -def git_version(service, version, branch, is_beta=False): - print(f"Pulling {service} v{version}") - subprocess.run( - f"git clone https://github.com/frappe/{service} --branch {branch}", shell=True - ) - cd = os.getcwd() - os.chdir(os.getcwd() + f"/{service}") - subprocess.run("git fetch --tags", shell=True) - - # XX-beta becomes XX for tags search - version = version.split("-")[0] - - version_tag = ( - subprocess.check_output( - f"git tag --list --sort=-version:refname \"v{version}*\" | sed -n 1p | sed -e 's#.*@\(\)#\\1#'", - shell=True, - ) - .strip() - .decode("ascii") - ) - - if not is_beta: - version_tag = version_tag.split("-")[0] - - os.chdir(cd) - return version_tag - - -def build(service, tag, image, branch): - build_args = f"--build-arg GIT_BRANCH={branch}" - if service == "erpnext": - build_args += f" --build-arg IMAGE_TAG={branch}" - if image == "nginx" and branch == "version-11": - build_args += f" --build-arg NODE_IMAGE_TAG=10-prod" - - print(f"Building {service} {image} image") - subprocess.run( - f"docker build {build_args} -t {service}-{image} -f build/{service}-{image}/Dockerfile .", - shell=True, - ) - tag_and_push(f"{service}-{image}", tag) - - -def tag_and_push(image_name, tag): - print(f'Tagging {image_name} as "{tag}" and pushing') - subprocess.run(f"docker tag {image_name} frappe/{image_name}:{tag}", shell=True) - subprocess.run(f"docker push frappe/{image_name}:{tag}", shell=True) - - -def main(): - args = parse_args() - tag = args.tag - branch = "develop" - - if args.version: - branch = "version-" + args.version - tag = git_version(args.service, args.version, branch, args.is_beta) - - if args.tag_only: - tag_and_push(f"{args.service}-{args.image_type}", tag) - else: - build(args.service, tag, args.image_type, branch) - - -if __name__ == "__main__": - main()