diff --git a/.travis.yml b/.travis.yml index d49df060d..f907fe867 100644 --- a/.travis.yml +++ b/.travis.yml @@ -205,7 +205,9 @@ jobs: script: ./scripts/travis/deploy/publish.sh "content-ce" "$DOCKER_HUB_REPOSITORY_DOMAIN" "$DOCKER_HUB_USERNAME" "$DOCKER_HUB_PASSWORD" - stage: Trigger DW - script: ./scripts/trigger-travis.sh --branch $TRAVIS_BRANCH Alfresco alfresco-apps $TRAVIS_API_TOKEN + script: + - ./scripts/trigger-travis.sh --branch $TRAVIS_BRANCH Alfresco alfresco-apps $TRAVIS_API_TOKEN + - ./scripts/travis/update/update-project.sh -p $TRAVIS_BUILD_NUMBER -t $GITHUB_TOKEN -v alpha -c $TRAVIS_COMMIT notifications: diff --git a/scripts/travis/update/aca-same-commit-verify.js b/scripts/travis/update/aca-same-commit-verify.js new file mode 100644 index 000000000..485591722 --- /dev/null +++ b/scripts/travis/update/aca-same-commit-verify.js @@ -0,0 +1,69 @@ +#!/usr/bin/env node + +const GitHub = require('github-api'); +let program = require('commander'); + +const ORGANISATION = 'Alfresco'; +const ORIGIN_REPO = 'alfresco-content-app'; + +class PrCreator { + constructor(githubUser, githubRepo, token, commit) { + this.github = new GitHub({token}); + this.repoOrigin = this.github.getRepo(githubUser, ORIGIN_REPO); + this.repoDestination = this.github.getRepo(githubUser, githubRepo); + this.commit = commit; + } + + async getShaClosedPr(head, base) { + return this.getShaPr(head, base, 'closed'); + } + + async getShaOpenPr(head, base) { + return this.getShaPr(head, base, 'open'); + } + + async getShaPr(head, base, status) { + const { data: closedUpstreamPRs } = await this.repoDestination.listPullRequests({ state: status, head: `${ORGANISATION}:${head}`, base }); + if (closedUpstreamPRs.length > 0) { + const latestClosedUpstream = closedUpstreamPRs[0]; + return latestClosedUpstream.body.split(':')[1].trim(); + } + return ''; + } + +} + +async function main() { + + program + .version('0.1.0') + .option('-t, --token [type]', 'token') + .option('-h, --head [type]', 'head') + .option('-r, --repo [type]', 'repo') + .option('-c, --commit [type]', 'commit') + .parse(process.argv); + + const { token, head, repo, commit } = program; + const prCreator = new PrCreator(ORGANISATION, repo, token, commit); + + const baseBranchName = 'develop'; + + const shaOpen = await prCreator.getShaOpenPr(head, baseBranchName); + const shaClosed = await prCreator.getShaClosedPr(head, baseBranchName); + if (shaOpen === commit || shaClosed === commit) { + console.log('ACA sha already exist'); + return 'true'; + } + return 'false'; +} + +main() + .then(result => { + process.stdout.write(result); + process.exit(0); + }) + .catch(error => { + console.error(error.response.status); + console.error(error.response.statusText); + process.exit(1); + }); diff --git a/scripts/travis/update/pr-creator.js b/scripts/travis/update/pr-creator.js new file mode 100644 index 000000000..58df4b306 --- /dev/null +++ b/scripts/travis/update/pr-creator.js @@ -0,0 +1,137 @@ +const GitHub = require('github-api'); +let program = require('commander'); + +const ORGANISATION = 'Alfresco'; +const ORIGIN_REPO = 'alfresco-content-app'; +const ATTEMPT_MSG = [ + `Could you check it please? 🤖`, + `Emm did you forget? 🤡`, + `Where are you? 🤷`, + `We are going to die!! 👻`, + `I guess the Apocalypse happened and I am alone 👽` +]; + +GIVE_UP_MSG = `I gave up, it will be fixed eventually 🔴`; + +class PrCreator { + constructor(githubUser, githubRepo, token, commit) { + this.github = new GitHub({token}); + this.repoOrigin = this.github.getRepo(githubUser, ORIGIN_REPO); + this.repoDestination = this.github.getRepo(githubUser, githubRepo); + this.issue = this.github.getIssues(githubUser, githubRepo); + this.commit = commit; + } + + async createOrUpdate(title, head, base, commit) { + const { data: prs } = await this.repoDestination.listPullRequests({ state: 'open', head: `${ORGANISATION}:${head}`, base }); + + if (prs.length < 1) { + const { data: pr } = await this.repoDestination.createPullRequest({ title, head, base, body: `sha:${commit}` }); + return pr.number; + } else { + const upstreamPrOpen = prs[0]; + // override the title to contains the latest aca dep number + await this.repoDestination.updatePullRequest(upstreamPrOpen.number, { title, body: `sha:${commit}` }); + return upstreamPrOpen.number; + } + + } + + async fetchContributors(shaFrom, shaTo) { + const mapAuthors = new Map(); + let upstreamShaFound = shaTo !== null; + const listCommits = await this.repoOrigin.listCommits(({sha: shaFrom})); + let index = 0; + while(upstreamShaFound) { + if (listCommits.data[index].sha === shaTo ) { + upstreamShaFound = false; + } else { + mapAuthors.set(listCommits.data[index].author.login, listCommits.data[index].commit.author.name); + } + index++; + } + return mapAuthors; + } + + async createComment(issueOrPrNumber, head, base, shaOriginHead ) { + const shaTo = await this.getShaTo(head, base); + const contributors = await this.fetchContributors(shaOriginHead, shaTo); + const attemptCount = await this.getCommentAmount(issueOrPrNumber); + const commentMsg = this.createCommentBody(contributors, attemptCount); + await this.issue.createIssueComment(issueOrPrNumber, commentMsg); + } + + createCommentBody(contributors, attemptCount) { + const flattenedContributors = this.flattenContributors(contributors); + const attemptMsg = attemptCount <= 5 ? ATTEMPT_MSG[attemptCount] : GIVE_UP_MSG + const tmpl = ` Attempt: ${attemptCount+1} + you are part of the contributors: + ${flattenedContributors} + ${attemptMsg} + `; + return tmpl; + } + + flattenContributors(contributors) { + let names = []; + for (let key of contributors.keys()) { + names.push(`@${key}`) + } + return names.join(', '); + } + + async getShaTo(head, base) { + const { data: closedUpstreamPRs } = await this.repoDestination.listPullRequests({ state: 'closed', head: `${ORGANISATION}:${head}`, base }); + if (closedUpstreamPRs && closedUpstreamPRs.length > 0) { + const latestClosedUpstream = closedUpstreamPRs[0]; + const shaTo = latestClosedUpstream.body.split(':')[1].trim(); + return shaTo; + } + return null; + } + + async getCommentAmount(issueOrPrNumber) { + const { data: listComments } = await this.issue.listIssueComments(issueOrPrNumber); + return listComments.length; + } +} + +async function main() { + + program + .version('0.1.0') + .option('-t, --token [type]', 'token') + .option('-h, --head [type]', 'head') + .option('-r, --repo [type]', 'repo') + .option('-c, --commit [type]', 'commit') + .option('-title, --title [type]', 'title') + .parse(process.argv); + + const { token, title, head, repo, commit } = program, + prCreator = new PrCreator(ORGANISATION, repo, token, commit); + + if (!token || !head || !title) { + throw new Error('Each of the parameters have to be provided. --token, --title, --head'); + } + const baseBranchName = 'develop'; + + const prNumber = await prCreator.createOrUpdate(title, head, baseBranchName, commit); + await prCreator.createComment(prNumber, head, baseBranchName, commit); + + return prNumber; +} + +main() + .then(prNumber => { + console.log("======= PR Created ========="); + + console.log(prNumber) + process.exit(0); + }) + .catch(error => { + + console.error("======= Impossible create PR ========="); + console.error(error.response.status); + console.error(error.response.statusText); + process.exit(1); + }); diff --git a/scripts/travis/update/update-project.sh b/scripts/travis/update/update-project.sh new file mode 100755 index 000000000..1d79216e3 --- /dev/null +++ b/scripts/travis/update/update-project.sh @@ -0,0 +1,133 @@ +!/usr/bin/env bash + +git config --global user.name "alfresco-build" +git config --global user.email "alfresco-build@hyland.com" + +BUILD_PIPELINE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +REPO_DIR="$BUILD_PIPELINE_DIR/../.." + +TEMP_GENERATOR_DIR=".tmp-generator"; +BRANCH_TO_CREATE="update-alfresco-aca-dependencies" +TOKEN="" +PR_NUMBER="" +DRY_RUN="false" + +show_help() { + echo "Usage: update-project.sh" + echo "" + echo "-t or --token: Github ouath token" + echo "-p or --pr: Originating ACA PR number" + echo "-v or --version version to update" + echo "-d or --dry-run: The script won't execute critical operation, just simulate them" +} + +set_token() { + TOKEN=$1 +} + +set_pr() { + PR_NUMBER=$1 +} + +version() { + VERSION=$1 +} + +set_commit() { + COMMIT=$1 +} + +set_dryrun() { + + DRY_RUN="true" + +} + +update_dependency() { + PKG=$1 + PKG_VERSION=$(npm view $PKG@$VERSION version) + echo "Update $PKG to $PKG_VERSION in $NAME_REPO" + + for i in $(find . ! -path "*/node_modules/*" -name "package-lock.json" | xargs grep -l $PKG); do + directory=$(dirname $i) + echo "Update $PKG in $directory" + ( cd $directory ; npm i --ignore-scripts $PKG@$PKG_VERSION --save-exact) + done + + git add . + git commit -n -m "[ci:force][auto-commit] Update $PKG to $PKG_VERSION for branch: $BRANCH_TO_CREATE originated from $PKG PR: $PR_NUMBER" +} + +update() { + NAME_REPO=$1 + PKG_VERSION=$(npm view $PKG@$VERSION version) + echo "Update dependencies $NAME_REPO" + + git clone https://$TOKEN@github.com/Alfresco/$NAME_REPO.git $TEMP_GENERATOR_DIR + cd $TEMP_GENERATOR_DIR + + git fetch + + # Checkout branch if exist, otherwise create it + BRANCH_CREATED=false + if git checkout $BRANCH_TO_CREATE 2>/dev/null ; then + git reset --hard origin/develop + else + BRANCH_CREATED=true + git checkout -b $BRANCH_TO_CREATE origin/develop + fi + + update_dependency "@alfresco/aca-shared" + update_dependency "@alfresco/aca-content" + update_dependency "@alfresco/aca-about" + update_dependency "@alfresco/aca-preview" + update_dependency "@alfresco/aca-viewer" + update_dependency "@alfresco/aca-folder-rules" + update_dependency "@alfresco/adf-office-services-ext" + + if [ "$BRANCH_CREATED" = true ]; then + git push origin $BRANCH_TO_CREATE + else + git push --force origin $BRANCH_TO_CREATE + fi + + node $BUILD_PIPELINE_DIR/pr-creator.js --token=$TOKEN --title="Update branch for ACA ${PKG_VERSION} [ci:force]" --head=$BRANCH_TO_CREATE --repo=$NAME_REPO --commit=$COMMIT + + cd .. + rm -rf $TEMP_GENERATOR_DIR +} + +while [[ $1 == -* ]]; do + case "$1" in + -h|--help|-\?) show_help; exit 0;; + -t|--token) set_token $2; shift; shift;; + -p|--pr) set_pr $2; shift; shift;; + -v|--version) version $2; shift 2;; + -c|--commit) set_commit $2; shift 2;; + -d|--dry-run) set_dryrun $2; shift; shift;; + -*) echo "invalid option: $1" 1>&2; show_help; exit 1;; + esac +done + +cd "$REPO_DIR" + +if [[ (-z "$TOKEN") || (-z "$VERSION") ]] + then + echo "Each of 'branch name' (-b) token (-t) and pr number (-p) have to be set. See -help." + exit 1; +fi + +rm -rf $TEMP_GENERATOR_DIR + +isSameACASha=$(node $BUILD_PIPELINE_DIR/aca-same-commit-verify.js --token=$TOKEN --head=$BRANCH_TO_CREATE --repo=$NAME_REPO --commit=$COMMIT ) +if [ "$isSameACASha" = 'true' ]; then + echo 'ACA sha is the same. No need to create another pr' + else + if [ "$DRY_RUN" = "false" ]; then + update "alfresco-apps" + else + echo "[dry-run] it would have update repos: 'alfresco-apps'" + fi +fi + +exit $?