TYPO3: How to publish an extension to TER with Github actions and tailor and add third party library on the fly - typo3

I would like to publish an extension automatically to TER by using the Github actions and tailor, the CLI Tool for maintaining public TYPO3 Extensions . This works perfectly fine with the following workflow configuration:
name: TYPO3 Extension TER Release
on:
push:
tags:
- '*'
jobs:
publish:
name: Publish new version to TER
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-20.04
env:
TYPO3_EXTENSION_KEY: ${{ secrets.TYPO3_EXTENSION_KEY }}
TYPO3_API_TOKEN: ${{ secrets.TYPO3_API_TOKEN }}
steps:
- name: Checkout repository
uses: actions/checkout#v2
- name: Check tag
run: |
if ! [[ ${{ github.ref }} =~ ^refs/tags/[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$ ]]; then
exit 1
fi
- name: Get version
id: get-version
run: echo ::set-output name=version::${GITHUB_REF/refs\/tags\//}
- name: Get comment
id: get-comment
run: |
readonly local comment=$(git tag -n10 -l ${{ steps.get-version.outputs.version }} | sed "s/^[0-9.]*[ ]*//g")
if [[ -z "${comment// }" ]]; then
echo ::set-output name=comment::Released version ${{ steps.get-version.outputs.version }} of ${{ env.TYPO3_EXTENSION_KEY }}
else
echo ::set-output name=comment::$comment
fi
- name: Setup PHP
uses: shivammathur/setup-php#v2
with:
php-version: 7.4
extensions: intl, mbstring, json, zip, curl
tools: composer:v2
- name: Install tailor
run: composer global require typo3/tailor --prefer-dist --no-progress --no-suggest
- name: Publish to TER
run: php ~/.composer/vendor/bin/tailor ter:publish --comment "${{ steps.get-comment.outputs.comment }}" ${{ steps.get-version.outputs.version }}
Since my extension depends on a third party PHP library (which is loaded if the extension is installed with composer) I need to add this library on the fly, when the extension gets deployed to the TER. Therefore I added in Resources/Private/PHP/ a composer.json and a composer.lock.
Now I would like to tell tailor to execute composer install in Resources/Private/PHP/ and package the extension including the external library. Is this possible? If so, how?

Generally it is useful to put everything in Composer command scripts so that you are actually able to execute actions without depending on Github actions or a CI in general.
For example you could add a few commands like this:
{
"scripts": {
"build:cleanup": [
"git reset --hard",
"git clean -xfd"
],
"deploy:ter:setup": [
"#composer global require clue/phar-composer typo3/tailor"
],
"build:ter:vendors": [
"(mkdir -p /tmp/vendors && cd /tmp/vendors && composer require acme/foo:^1.0 acme/bar:^2.0 && composer global exec phar-composer build -v)",
"cp /tmp/vendors/vendors.phar ./Resources/Private/libraries.phar",
"echo \"require 'phar://' . \\TYPO3\\CMS\\Core\\Utility\\ExtensionManagementUtility::extPath('$(composer config extra.typo3/cms.extension-key)') . 'Resources/Private/libraries.phar/vendor/autoload.php';\" >> ext_localconf.php"
],
"deploy:ter:upload": [
"composer global exec -v -- tailor ter:publish --comment \"$(git tag -l --format='%(contents)' $TAG)\" $TAG"
],
"deploy:ter": [
"#build:cleanup",
"#deploy:ter:setup",
"#build:ter:vendors",
"#deploy:ter:upload"
]
}
}
The various scripts explained:
build:cleanup drops all pending files to ensure no undesired files are uploaded to the TER
build:ter:setup installs typo3/tailor for the TER upload and clue/phar-composer for building a Phar of your vendor dependencies
build:ter:vendors installs a manually maintained list of dependencies in a temporary directory and builds a Phar from that; it then copies that Phar to the current directory and adds a require call to your ext_localconf.php
deploy:ter:upload finally invokes Tailor to upload the current directory including the Phar and the ext_localconf.php adjustment and fetches the comment of the specified Git tag; notice that tagging should be done with --message here to have an annotated Git tag (e.g. git tag -a 1.2.3 -m "Bugfix release")
Now assuming you export/provide the environment variables TYPO3_API_USERNAME, TYPO3_API_PASSWORD and TAG you can instantly deploy the latest release like this:
# Provide username and password for TER, if not done yet
export TYPO3_API_USERNAME=YourName
export TYPO3_API_PASSWORD=YourSecretPassword
# Provide the tag to deploy
TAG=1.2.3 composer deploy:ter
Subsequently the related Github action becomes very simple:
jobs:
build:
# ...
release-ter:
name: TYPO3 TER release
if: startsWith(github.ref, 'refs/tags/')
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout#v3
- name: Deploy to TER
env:
TYPO3_API_USERNAME: ${{secrets.TYPO3_API_USERNAME}}
TYPO3_API_PASSWORD: ${{secrets.TYPO3_API_PASSWORD}}
TAG: ${{github.ref_name}}
run: composer deploy:ter
Here is a live example:
.github/workflows/ci.yml
composer.json

Related

Releasing and Publishing via GH actions

I am trying to automate publishing the SDKs for Python, Java, GO, and Node. My main goal is to make the CI run whenever a new PR is created against main branch that will:
bump the version in all files.
publish the new release to the related public registry (for each language)
Problem:
right now the problem is that the publish step is not taking the artifacts from the release step, but rather the one before that, as if they are not synced.
For the release step, we're using semantic-release package with several plugins.
The ADMIN_TOKEN is a personal token of a user with write permissions.
The publishing step is different for each language, but I am certain this is unrelated since it worked before I complicated the workflow.
Possible issue:
Without the if statements, the release and publish steps are synced, but then the semantic-release creates another commit that creates another release (e.g. 2 releases and publishing in one run, not wanted). With the current if, the publish step takes the older release instead the newly created one (for example, if the new run creates release 1.0.40, the publish will take version 1.0.39).
Does anyone have some input on these 2 steps or the if statements? For example, this is the current variation of the Java workflow:
release:
runs-on: ubuntu-latest
if: "!startsWith(github.event.head_commit.message, 'chore')"
steps:
- name: Checkout code
uses: actions/checkout#v3
with:
fetch-depth: 0
token: ${{ secrets.ADMIN_TOKEN }}
- name: setup nodejs
uses: actions/setup-node#v3
with:
node-version: '16'
- name: release using semantic-release
env:
GITHUB_TOKEN: ${{ secrets.ADMIN_TOKEN }}
GIT_AUTHOR_NAME: ****
GIT_AUTHOR_EMAIL: ****
GIT_COMMITTER_NAME: ****
GIT_COMMITTER_EMAIL: ****
run: |
sudo apt-get update
sudo apt-get install python
pip install --user bumpversion
npm install #semantic-release/changelog
npm install #semantic-release/exec
npm install #semantic-release/git
npm install #semantic-release/github
npx semantic-release
publish:
runs-on: ubuntu-latest
needs: [release]
if: "!startsWith(github.event.head_commit.message, 'chore')"
steps:
- name: Checkout code
uses: actions/checkout#v3
with:
token: ${{ secrets.ADMIN_TOKEN }}
- name: Configure GPG Key
run: |
cat <(echo -e "${{ secrets.GPG_SIGNING_KEY }}") | gpg --batch --import
gpg --list-secret-keys --keyid-format LONG
- name: Set up Maven Central Repository
uses: actions/setup-java#v3
with:
java-version: 8
distribution: zulu
server-id: ossrh
server-username: ${{ secrets.MAVEN_USERNAME }}
server-password: ${{ secrets.MAVEN_PASSWORD }}
gpg-passphrase: ${{ secrets.GPG_PASSPHRASE }}
- name: Publish package
run: mvn clean deploy $MVN_ARGS -P central --no-transfer-progress --batch-mode -Dgpg.passphrase=${{ secrets.GPG_PASSPHRASE }}
env:
MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
MVN_ARGS: "--settings build-settings.xml"
<more ENVS>
In case it is relevant, the .releaserc file is:
{
"debug": true,
"branches": [ "main" ],
"plugins": [
["#semantic-release/commit-analyzer", {
"preset": "angular",
"releaseRules": [
{"type": "release","release": "patch"}
]}],
"#semantic-release/release-notes-generator",
"#semantic-release/changelog",
[
"#semantic-release/exec",
{
"prepareCmd": "bump2version --allow-dirty --current-version ${lastRelease.version} --new-version ${nextRelease.version} patch"
}
],
[
"#semantic-release/git",
{
"message": "chore(release): ${nextRelease.version} release notes\n\n${nextRelease.notes}"
}
],
"#semantic-release/github"
]
}
I also asked in GH: https://github.com/orgs/community/discussions/40749
The quick fix I found is to split the release and publish steps into two different workflows (different files). I am certain with a bit more dive-in, one can merge those two with some proper if conditioning.
NOTE: The publish action steps are specific to Java, but can be changed to be valid for any other language. The main structure is the main answer here.
The release step:
The semantic-release creates a secondary commit to the main branch with "chore" commit message. in order to overcome this, I added the if to skip this type of commit.
name: release
on:
workflow_dispatch:
push:
branches:
- main
jobs:
release:
runs-on: ubuntu-latest
if: "github.event_name == 'push' && github.ref == 'refs/heads/main' && !startsWith(github.event.head_commit.message, 'chore')"
steps:
- name: Checkout code
uses: actions/checkout#v3
with:
fetch-depth: 0
token: ${{ secrets.ADMIN_TOKEN }}
- name: setup nodejs
uses: actions/setup-node#v3
with:
node-version: '16'
- name: release using semantic-release
env:
GITHUB_TOKEN: ${{ secrets.ADMIN_TOKEN }}
GIT_AUTHOR_NAME: secrets.automation.dev
GIT_AUTHOR_EMAIL: secrets.automation.dev#il.ibm.com
GIT_COMMITTER_NAME: secrets.automation.dev
GIT_COMMITTER_EMAIL: secrets.automation.dev#il.ibm.com
run: |
sudo apt-get update
sudo apt-get install python
pip install --user bumpversion
npm install #semantic-release/changelog
npm install #semantic-release/exec
npm install #semantic-release/git
npm install #semantic-release/github
npx semantic-release
The publish step:
The "release" event has several initiators so I added the published type to make sure the publishing happens only if a new release was published to GitHub.
name: publish artifact
on:
workflow_dispatch:
release:
types: [published]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout#v3
with:
token: ${{ secrets.ADMIN_TOKEN }}
- name: Configure GPG Key
run: |
cat <(echo -e "${{ secrets.GPG_SIGNING_KEY }}") | gpg --batch --import
gpg --list-secret-keys --keyid-format LONG
- name: Set up Maven Central Repository
uses: actions/setup-java#v3
with:
java-version: 8
distribution: zulu
server-id: ossrh
server-username: ${{ secrets.MAVEN_USERNAME }}
server-password: ${{ secrets.MAVEN_PASSWORD }}
gpg-passphrase: ${{ secrets.GPG_PASSPHRASE }}
- name: Publish package
run: mvn clean deploy $MVN_ARGS -P central --no-transfer-progress --batch-mode -Dgpg.passphrase=${{ secrets.GPG_PASSPHRASE }}
env:
MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
MVN_ARGS: "--settings build-settings.xml"
<other envs>

Github Actions environment variables not being set on Linux

I'm trying to set my Github secrets as env variables via Actions but for some reasons they aren't being set (Using Ubuntu 22.04.1 LTS).
name: My app
on:
push:
branches: [ "main" ]
env:
JWT_SECRET_KEY: ${{ secrets.JWT_SECRET_KEY }}
JWT_REFRESH_SECRET: ${{ secrets.JWT_REFRESH_SECRET }}
MONGO_CONNECTION_STRING: ${{ secrets.MONGO_CONNECTION_STRING }}
PLAI_APP_URL: ${{ secrets.APP_URL }}
SENDGRID_API_KEY: ${{ secrets.SENDGRID_API_KEY }}
permissions:
contents: read
jobs:
build:
runs-on: self-hosted
steps:
- name: check out latest repo
uses: actions/checkout#v3
with:
ref: main
- name: Set up Python 3.10
uses: actions/setup-python#v3
with:
python-version: "3.10"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
Workflow runs successfully without errors but when I check my variables via env they are not listed. Any idea what I'm doing wrong?
You could follow the same idea as in "Managing secrets in an open-sourced flutter web app" from Mahesh Jamdade:
encode your confidential file to base64 and store the encoded output to GitHub secrets.
In your GitHub workflow decode the encrypted secret and convert it back to a file and then run your build scripts.
You can see an example in "How to access secrets when using flutter web with github actions".
Example workflow: maheshmnj/vocabhub .github/workflows/firebase-hosting-merge.yml.

Bump date-based tag in GitHub Actions

I'm trying to create automatic releases of a project using GitHub Actions where the release tag is based on the date. I don't want to use standard semantic versioning because this project is a fork of another project that uses date-based versioning. I've found posts about the getting the date in a workflow and have this so far:
name: Publish
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
publish:
name: Publish release of font files
runs-on: ubuntu-latest
steps:
- name: Create foo file
run: |
mkdir cascadios
> cascadios/foo.txt
- name: Get tag name from date
id: tag_name
run: echo "::set-output name=date::$(date +'%y%m.%d')"
- name: Zip font files
run: |
cd cascadios
zip ../cascadios-${{ steps.tag_name.outputs.date }}.zip *
cd ..
- name: Create tag
uses: mathieudutour/github-tag-action#v6.0
id: tag_version
with:
github_token: ${{ github.token }}
custom_tag: ${{ steps.tag_name.outputs.date }}
Note: I have it creating a blank file to release to save the 15 minutes of building.
This works great until I have two releases in one day (such as today where I am testing this workflow a lot). How can I get it so I can add a dynamic patch number to the tag, so it doesn't cause a conflict? An example might look like v2112.13.0.
You can use our versioning library - https://github.com/relizaio/versioning
Then you can declare version pattern to be something like: YYYY.0M.0D.Micro
Then the following command would produce a base version:
docker run --rm relizaio/versioning -s YYYY.0M.0D.Micro
Now, if you already have a version say 2021.12.14.1 and want to bump it up, you would do
docker run --rm relizaio/versioning -s YYYY.0M.0D.Micro -v 2021.12.14.1 -a bump
This would produce 2021.12.14.2 if you call it on December 14th, or something 2021.12.15.0 if you call this command on the December 15th.

github actions – where are the compilation results?

I have defined a little github action workflow, which is supposed to compile a kss-styleguide from scss.
The steps of that workflow basically trigger building the resulting css and the respective kss-styleguide.
When I run the build process locally on my dev machine the built styleguide is written to the styleguide folder located in the root of my project.
However on github, despite everything being marked off green, I don't know, what or where the resulting files are being written to.
How can I deploy the generated styleguide, if I don't know where it is?
Here's my yaml file for this workflow:
name: Node.js CI
on:
push:
branches: [ mk-node-ci ]
pull_request:
branches: [ mk-node-ci ]
jobs:
build:
name: Build Styleguide
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x]
steps:
- uses: actions/checkout#v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node#v1
with:
node-version: ${{ matrix.node-version }}
- uses: borales/actions-yarn#v2.0.0
with:
cmd: install
env:
NODE_ENV: development
- name: "build CSS files"
uses: borales/actions-yarn#v2.0.0
with:
cmd: "build:css"
- name: "build styleguide files"
uses: borales/actions-yarn#v2.0.0
with:
cmd: "build:styleguide"
Updated 2020.10.14 19:25 GMT
GitHub Actions are performed on a separate "clean" Runner Machine.
actions/checkout#v2 is an action that copies your repository to that machine — typically, to perform tests etc.
In order to get produced results (like modified files) from runner machine back to the original repository, we can use:
(1) upload-artifact action.
(2) git push.
For example, here is my script to modify files from the source directory and put them into the output directory (I run it as an action (bash script): - run: wrap.sh). The script wrap.sh:
echo "Copy directory structure from 'in' to 'out':";
find ./in -type d | while read i;
do
if [ ! -d "${i/in/out}" ]; then
mkdir "${i/in/out}"
echo "${i/in/out}";
fi
done
echo "Wrap files:";
find ./in -type f -name "*" | while read i;
do
echo "${i/in/out}";
cat ./tpl/header.html "$i" ./tpl/footer.html >"${i/in/out}"
git add "${i/in/out}"
done
git config user.name "chang-zhao"
git commit . -m "Wrapping"
git push origin main
Here git add "${i/in/out}" is adding to git a new file with that name. git config user.name "..." is required for the commit to work. git commit . -m "Wrapping" is the commit that puts new files into the repository ("Wrapping" is a name I gave to such commits).
This way files produced on a runner server get pushed to the original repository.

Can I make releases public from a private github repo?

I have an application which is in a private github repo, and am wondering if the releases could be made public so that the app can auto-update itself from github instead of us having to host it.
Additionally I'm wondering if it's possible to use the github api from the deployed app to check for new updates.
A workaround would be to create a public repo, composed of:
empty commits (git commit --allow-empty)
each commit tagged
each tag with a release
each release with the deliveries (the binaries of your private app)
That way, you have a visible repo dedicated for release hosting, and a private repos for source development.
As #VonC mentioned we have to create a second Repository for that. This is not prohibited and i am doing it already. With github workflows i automated this task, I'm using a develop / master branching, so always when I'm pushing anything to the master branch a new version is build and pushed to the public "Realease" Repo.
In my specific use case I'm building an android apk and releasing it via unoffical github api "hub". Some additional advantage of this is you can have an extra issue tracker for foreign issues and bugs.
name: Master CI CD
# using checkout#v2 instead of v1 caus it needs further configuration
on:
pull_request:
types: [closed]
jobs:
UnitTest:
runs-on: ubuntu-latest
if: github.event.pull_request.merged
steps:
- uses: actions/checkout#v2
- name: make executable
run: chmod +x gradlew
- name: Unit tests
run: |
./gradlew test
IncrementVersionCode:
needs: UnitTest
runs-on: ubuntu-latest
steps:
- uses: actions/checkout#v2
- name: set up JDK 1.8
uses: actions/setup-java#v1
with:
java-version: 1.8
- name: make executable
run: chmod +x gradlew
- name: increment version
run: ./gradlew incrementVersionCode
- name: Push new version to master
run: |
git config --local user.email "workflow#bot.com"
git config --local user.name "WorkflowBot"
git commit -m "Increment Build version" -a
# maybe better amend commits to avoid bot commits
BuildArtifacts:
needs: IncrementVersionCode
runs-on: ubuntu-latest
steps:
- uses: actions/checkout#v2
- name: set up JDK 1.8
uses: actions/setup-java#v1
with:
java-version: 1.8
- name: make executable
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew build -x lint
- name: Rename artifacts
run: |
cp app/build/outputs/apk/release/app-release.apk MyApp.apk
- name: Upload Release
uses: actions/upload-artifact#master
with:
name: Release Apk
path: MyApp.apk
- name: Upload Debug
uses: actions/upload-artifact#master
with:
name: Debug Apk
path: app/build/outputs/apk/debug/app-debug.apk
# https://dev.to/ychescale9/running-android-emulators-on-ci-from-bitrise-io-to-github-actions-3j76
E2ETest:
needs: BuildArtifacts
runs-on: macos-latest
strategy:
matrix:
api-level: [21, 27]
arch: [x86]
steps:
- name: checkout
uses: actions/checkout#v2
- name: Make gradlew executable
run: chmod +x ./gradlew
- name: run tests
uses: reactivecircus/android-emulator-runner#v2
with:
api-level: ${{ matrix.api-level }}
arch: ${{ matrix.arch }}
script: ./gradlew connectedCheck
Deploy:
needs: E2ETest
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master'
steps:
- uses: actions/checkout#v2 # Needed for gradle file to get version information
- name: Get Hub
run: |
curl -fsSL https://github.com/github/hub/raw/master/script/get | bash -s 2.14.1
cd bin
chmod +x hub
cd ..
- name: Get Apk
uses: actions/download-artifact#master
with:
name: Release Apk
- name: Publish
env:
GITHUB_TOKEN: "${{ secrets.RELEASE_REPO_SECRET }}"
run: |
APP_NAME=MyApp
VERSION_NAME=`grep -oP 'versionName "\K(.*?)(?=")' ./app/build.gradle`
VERSION_CODE=`cat version.properties | grep "VERSION_CODE" | cut -d'=' -f2`
FILENAME="${APP_NAME}-v${VERSION_NAME}-${VERSION_CODE}"
TAG="v${VERSION_NAME}-${VERSION_CODE}"
TAG="latest-master"
echo $APP_NAME
echo $VERSION_NAME
echo $VERSION_CODE
echo $FILENAME
echo $TAG
git clone https://github.com/MyUser/MyApp-Releases
cd MyApp-Releases
./../bin/hub release delete "${TAG}" || echo "Failed deleting TAG: ${TAG}" # If release got lost catch error with message
./../bin/hub release create -a "../${APP_NAME}.apk" -m "Current Master Build: ${FILENAME}" -p "${TAG}"
EvaluateCode:
needs: Deploy
runs-on: ubuntu-latest
steps:
- name: Get Hub
run: |
echo "TDOO: Run Jacoco for coverage, and other profiling tools"
The 2022 answer to this question is even more straight-forward.
You'd just need to use the pre-installed gh CLI:
gh release create v0.0.1 foobar.zip -R https://github.com/your/repo-here
This command will create a tag v0.0.1 and a release with the local file foobar.zip attached on the public repository. You can run this in the GitHub Action of any private repository.
The -R argument points to the repository you'd like to create a tag/release on. foobar.zip would be located in your local directory.
One thing is important here: GITHUB_TOKEN must still be set as the token of the repository you'd like to release on!
Full example:
- name: Publish
env:
GITHUB_TOKEN: "${{ secrets.RELEASE_REPO_SECRET }}"
run: |
gh release create v0.0.1 foobar.zip -R https://github.com/your/repo-here
If you're planning to re-release and override existing versions, there is gh release delete as well. The -d flag creates a release as a draft etc. pp. Please take a look at the docs.
I'm using a slightly more advanced approach by setting:
shell: bash
run: $GITHUB_ACTION_PATH/scripts/publish.sh
And in file scripts/publish.sh:
#!/usr/bin/env node
const cp = require('child_process')
const fs = require('fs');
const path = require('path');
const APP_VERSION = JSON.parse(fs.readFileSync('package.json', { encoding: 'utf8' })).version
const TAG = `v${APP_VERSION}`
cp.execSync(`gh release create ${TAG} foobar.zip -R https://github.com/your/repo-name`, { stdio: 'inherit' })
This approach enables you to be able to for example, use Node.js or any other programming language available, to extract a version from the project management file of choice (e.g. package.json) and automatically come up with the right tag version and name.
A simple way to duplicate releases from a private repo to a public one may be this Release & Assets Github Action which can: Create a release, upload release assets, and duplicate a release to other repository.
Then you can use the regular electron-builder updater's support for public repos.