In a recent post How can We use GitHub Actions in Gitlab?, we looked at the idea, how both services could the use same CI recipe. As a Gitlab CI user, you can use some GitHub Actions to do the work for you.
Today we look at how to write such action to provide an excellent developer experience for both.
"If you want to go fast, go alone.
If you want to go far, go together."
5 years ago I gave a talk [Czech only] about 2 frameworks. These 2 frameworks were two different groups that didn't like each other. Both frameworks were written in PHP, both MVC and both were used by PHP developers. I was wondering, why not go together?
My talk was about how to convert this aversion into a healthy competition, friendship, and mutual learning.
What movie does that resemble?
I would love to see a similar story between "Old Gitlabhand" and "GitHubtou" :). While researching Gitlab and GitHub in this theme, I found a post from 2018 called GitLab CI/CD for GitHub by Andreas Offenhauser. The idea in the post is amazingly simple.
Back in 2018, GitHub didn't have a proper CI yet. We used more matured Travis, Circle CI, etc. Andreas suggests, we can use the best of both worlds - hook in GitHub to Gitlab CI pipelines and let GitHub collect Gitlab CI feedback.
The rest is history, but the underlying philosophy goes beyond time.
We already know how to write .gitlab-ci.yml
so we can use GitHub Action. But how can we write GitHub Action without writing two scripts - one for GitHub and another for Gitlab?
First, we need to use the Docker approach. The GitHub docs suggest using Docker with arguments:
docker run some-image $ARGUMENTS
Then work with arguments by their order - $1, $2 etc. In practise the GitHub workflow might look like this:
# .github/workflows/monorepo_split.yaml
# ...
with:
from-package: 'packages/easy-coding-standard'
to-repository: 'https://github.com/symplify/easy-coding-standard'
The philosophy of Gitlab is a bit different. They are closer to Docker ideology, so instead of argument order, they promote ENV variables.
# .gitlab-ci.yml
env:
FROM_PACKAGE: "packages/easy-coding-standard"
TO_REPOSITORY: "https://github.com/symplify/easy-coding-standard"
You can find this approach in Postgres Docker, Mysql Docker, and many others.
So Github script works with arguments order and Gitlab with ENV variables. That's a pickle. What now? Do we have to write two scripts to make our GitHub Action work for both?
When I spoke about Nette and Symfony to both communities, I focused on values they share - active community, simple controller architecture, creative solutions in small packages. This way, we could find understanding from each other.
What would be the shared path here?
The default way of doing things suggests there are also alternative ways. You can use magic facades in Laravel by default, or you can use constructor injection alternative. I was looking for such an alternative for a while in GitHub and Gitlab, so they can bridge together.
After a couple of days of frustration, I came across Create custom Github Action in 4 steps post.
There was one very important sentence:
inputs
: defines the input parameters you can pass into your bash script.
$INPUT_{Variable}
in our example $INPUT_POKEMON_ID
So does that mean that this GitHub Action input:
with:
from-package: 'packages/easy-coding-standard'
to-repository: 'https://github.com/symplify/easy-coding-standard'
is also:
env:
INPUT_FROM_PACKAGE: 'packages/easy-coding-standard'
INPUT_TO_REPOSITORY: 'https://github.com/symplify/easy-coding-standard'
Can you see the shared pattern? GitHub is using ENV, Gitlab is using ENV...
Heureka! We've found it!
✅
The final solution is straightforward:
INPUT_
$env = getenv();
$ciPlatform = '...'; // detect via known ci-based ENV variables
$envPrefix = $ciPlatform === 'GITHUB' ? 'INPUT_' : '';
// shared input
$packageDirectory = $env[$envPrefix . 'FROM_DIRECTORY'];
$toRepository = $env[$envPrefix . 'TO_REPOSITORY'];
In the end, the Docker image has one script for both. You would not even notice what CI service it was written for originally.
So next time you'll be writing a GitHub Action, think of your friends in Gitlab and write a Docker image for them too. Thank you.
Happy coding!
Do you learn from my contents or use open-souce packages like Rector every day?
Consider supporting it on GitHub Sponsors.
I'd really appreciate it!