Image by Randall Munroe, xkcd.com
- Upgrading PHP packages only using composer
- Packages following semantic versioning only
- compatible updates only (minor and patch versions)
- Using GitLab with runner that uses the Docker executor
- more Security
- less Bugs
- new Features to make use of
- no time given from management
- fear of braking something ("newer change a running system")
- not using composer at all
- missing awareness for security
- wrong priorities
- ...
- create a ticket with a due date
- pull the ticket and assign it to me
- create a merge request / pull request and a new branch
- checkout the master branch locally
- run
composer update
- run
composer bump
- run
phpunit
- wait until test finish
- checkout the new update branch locally
- copy updated packages from composer output to clipboard
- run
git add composer.json composer.lock
- run
git commit
- fill in commit message using "Update PHP dependencies week xx" + composer output from clipboard
- run
git push
- open the browser tab with the merge request
- wait for the CI pipeline
- mark the MR as ready
- assign a reviewer
- move the ticket to the next column for code review
- wait for the reviewer to review and approve the change
- merge the branch and close the MR
- wait for the CI/CD pipeline
- create a new ticket with due date next week
- allow compatible update of packages in composer.json using
^
"laravel/vapor-core": "^2.32.0", "laravel/vapor-ui": "^1.7.4", "league/flysystem-aws-s3-v3": "^1.0.30", "maatwebsite/excel": "^3.1.48", "mews/purifier": "^3.4.1", "sentry/sentry-laravel": "^3.8.0", "spatie/laravel-activitylog": "^3.17",
- pin versions for packages which must not be upgraded
- check for 0.x versions, as composer will NOT upgrade these
- Specify the proper target platform in composer.json
"config": { "platform": { "php": "8.0.21" },
- automated tests with decent code coverage
- error and performance monitoring / alerting like Sentry or NewRelic
- ability to rollback a deployment quickly
- Create a GitLab Acess-Token with API permission for a GitLab user
- Create a GitHub token
- In the .gitlab-ci.yml add a new stage e.g.
update-dependencies
stages: - update-dependencies - code-check - build - tests - deploy
In the .gitlab-ci.yml add a new job for the new stage with the property only: - schedules
:
composer-update:
stage: update-dependencies
only:
- schedules
image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/composer:2.5.8
script:
- "composer update --no-ansi --no-interaction --no-scripts --no-install --no-audit
--no-progress --ignore-platform-req=ext-* 2>&1 | tee composer-out.txt"
- composer bump
- git config --global user.email "some.email@example.com"
- git config --global user.name "Some Name"
- git config --global --add safe.directory $CI_PROJECT_DIR
- BRANCH_NAME="update-php-dpendencies-$(date '+%Y-%m-%d_%H-%M')"
- git checkout -b $BRANCH_NAME
- git add composer.json composer.lock
- (echo "Automated PHP dependency upgrades $(date '+%Y-%m-%d')" ; echo "";) | tee commit-message.txt
- | # extract list of updated packages and save to a file and a variable
UPGRADED_PACKAGES=$(cat composer-out.txt | \
sed -n '/^Writing lock file/ q; p' | \
sed -n '/Updating dependencies/,$ p' | \
sed '1 d' | tee -a commit-message.txt | \
sed 's/$/\\n/'| tr -d '\n'); # convert to a single line for json
- echo $UPGRADED_PACKAGES
- git commit -F commit-message.txt
- git push "https://${GITLAB_USER_LOGIN}:${GITLAB_ACCESS_TOKEN}@${CI_REPOSITORY_URL#*@}" "HEAD:$BRANCH_NAME"
- |
BODY="{
\"id\": ${CI_PROJECT_ID},
\"description\": \"${UPGRADED_PACKAGES}\",
\"source_branch\": \"${BRANCH_NAME}\",
\"target_branch\": \"master\",
\"remove_source_branch\": true,
\"title\": \"Automated PHP dependency upgrades $(date '+%Y-%m-%d')\"
}";
- |
curl --fail --no-progress-meter -X POST "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/merge_requests" \
--header "PRIVATE-TOKEN:${GITLAB_ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--data "${BODY}"
Important: all other jobs must get the property except: - schedules
syntax-check:
stage: code-style
image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/php:8.0.21-cli-alpine
except:
- schedules
script:
- find . -type f -name '*.php' -print0 | xargs -0 -n1 -P4 php -l -n | (! grep -v "No syntax errors detected" )
Create a Scheduled Task
To test the job click the play button:
Then open CI/CD pipelines and open the latest job
As a result, a new Merge Request is created, ready to be reviewed, merged and deployed:
- abort if there are no upgrades (Nothing to modify in lock file)
- automate removal of outdated MRs and branches
- use variables for git user name and email
- make a GitHub action
- upgrade node_modules too
- merge automatically
- extract to a central yml file to be used by all repositories
- GitHub dependabot
- https://www.mend.io/renovate/
- ...
- Example .gitlab-ci.yml file:
https://gist.github.com/oliworx/e84550dda4c5953189b94d36f002e58e - Create MR via GilLab API:
https://archives.docs.gitlab.com/14.10/ee/api/merge_requests.html#create-mr - GitLab CI: Auto dependency updates:
https://thacoon.com/posts/gitlab-ci-auto-dependency-update/ - How to automatically create a new MR on GitLab with GitLab CI:
https://about.gitlab.com/blog/2017/09/05/how-to-automatically-create-a-new-mr-on-gitlab-with-gitlab-ci/ - Auto Merge Dependabot Pull Requests with GitHub Actions:
https://stefanzweifel.dev/posts/2021/04/28/auto-merge-dependabot-pull-requests