-
Notifications
You must be signed in to change notification settings - Fork 80
Gradle, Travis CI and Maven Central
This is a list of the steps to perform to publish a GitHub open source project to Maven Central, in a mostly automated fashion, using Travis CI. Atlas is a working example: osmlab/atlas. Most examples below are from that project.
This document assumes that the project
- is in github.com
- is not private
- has an open license
- is built using gradle
- is written in Java/Scala
The repository should have a locked master
branch, which will be used by Travis to build snapshots and releases, and a default branch called dev
.
If not there already, make sure to enable the gradle wrapper, so Travis does not have to guess about the version of Gradle to use.
gradle wrapper
This creates a gradlew and gradlew.bat files:
Sonatype is the company that maintains the Maven Central repository, and they offer free hosting for open source software. They also take care of the workflow of syncing the releases with the maven central mirror.
Open https://issues.sonatype.org and create an account in JIRA (that username/password will also be the login credentials to upload artifacts).
Open a JIRA ticket with Sonatype to open the group id for you: https://issues.sonatype.org/secure/CreateIssue.jspa?issuetype=21&pid=10134
Fill all the required info, and wait for a human to resolve the issue. It should then look like this: https://issues.sonatype.org/browse/OSSRH-31007
In build.gradle
, add tasks for the javadoc and sources:
apply plugin: 'java'
task javadocJar(type: Jar) {
classifier = 'javadoc'
from javadoc
}
task sourcesJar(type: Jar) {
classifier = 'sources'
from sourceSets.main.allSource
}
artifacts
{
archives javadocJar, sourcesJar
}
In gradle.properties
, on top of the group and version, add the following (and replace the values accordingly). This will enable Gradle to create a full pom file that Maven Central will approve of.
maven2_url=https://oss.sonatype.org/service/local/staging/deploy/maven2/
snapshot_url=https://oss.sonatype.org/content/repositories/snapshots/
project_name="OSM Atlas Library"
project_description="Library to load OSM data into an Atlas format"
project_url=https://github.com/osmlab/atlas
project_license_url=https://github.com/osmlab/atlas/blob/master/LICENSE
project_license_slug="BSD 3 Clause"
project_developer=matthieun
project_scm=scm:git:https://github.com/osmlab/atlas.git
Then, in build.gradle
, use the maven plugin to format the pom so Maven Central accepts it.
apply plugin: 'maven'
uploadArchives
{
repositories
{
mavenDeployer
{
beforeDeployment
{
MavenDeployment deployment -> signing.signPom(deployment)
}
repository(url: maven2_url) {
authentication(userName: System.getenv('SONATYPE_USERNAME'), password: System.getenv('SONATYPE_PASSWORD'))
}
snapshotRepository(url: snapshot_url) {
authentication(userName: System.getenv('SONATYPE_USERNAME'), password: System.getenv('SONATYPE_PASSWORD'))
}
pom.project
{
name project_name
packaging 'jar'
description project_description
url project_url
scm {
connection project_scm
developerConnection project_scm
url project_url
}
licenses {
license {
name project_license_slug
url project_license_url
}
}
developers {
developer {
id project_developer
name project_developer
}
}
}
}
}
}
Maven central being a trusted source of artifacts, it requires you to sign the artifacts to guarantee their authenticity.
GnuPG is a free PGP keyring manager. It will be used to sign the artifacts. This section details the steps to follow to sign artifacts for maven central. To get a more in-depth view of that process, visit http://central.sonatype.org/pages/working-with-pgp-signatures.html
brew install gpg
gpg --version
gpg --full-generate-key
Here are the options to follow during that process:
- size: 4096
- The email you used in the Sonatype account (not mandatory)
- Your github username
- A passphrase (to remember!)
Finally, list the keys:
gpg --keyid-format short --list-secret-keys
returns:
/Users/matthieun/.gnupg/pubring.gpg
-----------------------------------
sec rsa4096 2017-05-04 [SC]
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
uid [ultimate] username <email>
ssb rsa4096/AAAAAAAA 2017-05-04 [E]
sec rsa4096 2018-02-06 [SC]
YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
uid [ultimate] username <email>
ssb rsa4096/BBBBBBBB 2018-02-06 [E]
Here, the AAAAAAAA
that corresponds to the date/username/email just created above is the short keyring identifier. Save it to a note. It will have to be encrypted for Travis later.
gpg --export-secret-keys XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX > ~/Desktop/secret.gpg
That GPG file will later be encrypted by Travis.
For the signature to be authenticated, the public part of the keyring needs to be uploaded to a key server. Below, replace XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
with the keyring identifier.
gpg --keyserver hkp://pool.sks-keyservers.net --send-keys XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Gradle has a neat signing plugin that does all the work. Add the following to build.gradle
:
apply plugin: 'signing'
...
import org.gradle.plugins.signing.Sign
gradle.taskGraph.whenReady { taskGraph ->
if (taskGraph.allTasks.any { it instanceof Sign }) {
allprojects { ext."signing.keyId" = System.getenv('GPG_KEY_ID') }
allprojects { ext."signing.secretKeyRingFile" = System.getenv('GPG_KEY_LOCATION') }
allprojects { ext."signing.password" = System.getenv('GPG_PASSPHRASE') }
}
// Do not sign archives by default (a local build without gpg keyring should succeed)
if (taskGraph.allTasks.any { it.name == 'build' || it.name == 'assemble' }) {
tasks.findAll { it.name == 'signArchives' || it.name == 'signDocsJar' || it.name == 'signTestJar' }.each { task ->
task.enabled = false
}
}
}
signing
{
sign configurations.archives
}
build.dependsOn.remove(signArchives)
The signing task depends on build by default. We make sure to exclude that, as we do not want to sign artifacts all the time. When a user wants to build the project, he should not have to build a keyring first. Signing is useful at deployment only.
The CI tool will then call "gradle signArchives" directly, which will enable the task and run it.
Travis CI is a continuous integration tool. It is free and unlimited for all open source projects.
Use GitHub to log into Travis CI:
Then, go to Github. Settings > Authorized applications
You should see Travis as an Authorized application:
Back to Travis CI, go to the settings page (default page, of if you have enabled projects before, the "+" button next to "my repositories" on the left side)
Click sync account:
Enable your project:
Install the travis command:
gem install travis
Navigate to the repository clone on your local system. Then login with travis:
travis login
Supply your github credentials when prompted. This allows the travis command line to encrypt some files and environment variables that will be visible in .travis.yml
.
So that Travis can access your github account within scripts you write, you need to create a Github application token:
Github > Settings > Personal access tokens > Generate new token
When the token is generated, make sure to copy it as it will be visible only once.
Then go back to the root of your project clone, and type the following (be careful, the "=" here is very important):
travis encrypt "GITHUB_SECRET_TOKEN=<the token you just copied>"
The command will reply with a long encrypted string: secure: "blah blah"
. Save that string in a note.
Create a .travis
folder that will hold all the automation scripts in the root of the project.
Create a .travis.yml
file at the root of the repo. Travis reads this file first to identify what environment and build steps it needs to run. More detailed documentation on the .travis.yml
file here.
env:
global:
- # GITHUB_SECRET_TOKEN
- secure: "XXXXXX"
- # SONATYPE_USERNAME
- secure: "XXXXXX"
- # SONATYPE_PASSWORD
- secure: "XXXXXX"
- # GPG_KEY_ID
- secure: "XXXXXX"
- # GPG_PASSPHRASE
- secure: "XXXXXX"
- GPG_KEY_LOCATION=".travis/secret.gpg"
- ENCRYPTED_GPG_KEY_LOCATION=".travis/secret.gpg.enc"
branches:
only:
- master
- dev
language: java
jdk:
- oraclejdk8
before_install:
- chmod -R ug+x .travis
- .travis/install.sh
script:
- chmod -R ug+x .travis
- .travis/build.sh
- .travis/merge-dev-to-master-gate.sh
- .travis/deploy-gate.sh
- .travis/tag-master-gate.sh
- The
global
section stores the encrypted environment variables that travis only will be able to decrypt. At that point, update the one calledGITHUB_SECRET_TOKEN
that was saved in a note just before. - The
branches
section lets Travis know which branches to build on. Usemaster
anddev
only. - The
before_install
step runs before the build starts. This is where decrypting the keys will happen. - The
script
step is the list of build steps. If any step fails, the next ones continue, but the build will be marked as failed.
The keyring secret file that was created before needs to be encrypted by travis.
travis encrypt-file ~/Desktop/secret.gpg
It will reply with an openssl command that looks like this:
openssl aes-256-cbc -K $encrypted_XXXXXX_key -iv $encrypted_XXXXXX_iv -in $ENCRYPTED_GPG_KEY_LOCATION -out $GPG_KEY_LOCATION -d
Copy that command, and paste it in a file called install.sh
in the .travis
folder. Very important: Make sure that the -in
and -out
arguments are adapted so they are in the .travis
folder, where Travis will find them at build time.
#!/usr/bin/env sh
if [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ];
then
openssl aes-256-cbc -K $encrypted_XXXXXX_key -iv $encrypted_XXXXXX_iv -in $ENCRYPTED_GPG_KEY_LOCATION -out $GPG_KEY_LOCATION -d
fi
Final step, the travis script also created an encrypted gpg file: secret.gpg.enc. Move it to the .travis folder.
The GPG keyring identifier (saved in a note in a previous step) needs to be encrypted.
travis encrypt "GPG_KEY_ID=<your key id>"
Paste the results to the .travis.yml
in the proper global section.
The GPG keyring passphrase (generated when the keyring was created) needs to be encrypted.
travis encrypt "GPG_PASSPHRASE=<your key passphrase>"
Paste the results to the .travis.yml
in the proper global section.
Sonatype credentials are used by Travis to publish snapshots and releases to maven central.
The Sonatype JIRA account name needs to be encrypted.
travis encrypt "SONATYPE_USERNAME=<your sonatype username>"
Paste the results to the .travis.yml
in the proper global section.
The Sonatype JIRA account password needs to be encrypted.
travis encrypt "SONATYPE_PASSWORD=<your sonatype password>"
Paste the results to the .travis.yml
in the proper global section.
The next steps describe the workflow Travis will follow for each CI cycle.
Add a build.sh
file to the .travis/
folder:
#!/usr/bin/env sh
chmod u+x gradlew
if [ "$TRAVIS_PULL_REQUEST" != "false" ];
then
echo "Skip integration tests in pull request builds"
./gradlew clean build -x integrationTest
else
./gradlew clean build
fi
This is the easy part. Go to the repository's settings in travis (click + on the left if you have run builds already, otherwise it is the main page).
Click the wheel between the enablement switch and the name:
In "General", select Build branch updates and build pull request updates.
At that point, pull requests will trigger builds.
Any commit to dev will trigger a build so far. In this section we want Travis to do the merge from dev to master automatically when a build is successful on dev.
Create a merge-dev-to-master.sh
script under .travis/
. Make sure to update the GITHUB_REPO
variable.
#!/usr/bin/env sh
GITHUB_REPO="osmlab/atlas"
MERGE_BRANCH=master
SOURCE_BRANCH=dev
FUNCTION_NAME="merge-$SOURCE_BRANCH-to-$MERGE_BRANCH"
echo "$FUNCTION_NAME: $GITHUB_REPO"
echo "$FUNCTION_NAME: TRAVIS_BRANCH = $TRAVIS_BRANCH"
echo "$FUNCTION_NAME: TRAVIS_PULL_REQUEST = $TRAVIS_PULL_REQUEST"
if [ "$TRAVIS_BRANCH" != "$SOURCE_BRANCH" ];
then
echo "$FUNCTION_NAME: Exiting! Branch is not $SOURCE_BRANCH: ($TRAVIS_BRANCH)"
exit 0;
fi
if [ "$TRAVIS_PULL_REQUEST" != "false" ];
then
echo "$FUNCTION_NAME: Exiting! This is a Pull Request: $TRAVIS_PULL_REQUEST"
exit 0;
fi
: ${GITHUB_SECRET_TOKEN:?"GITHUB_SECRET_TOKEN needs to be set in .travis.yml!"}
export GIT_COMMITTER_EMAIL="[email protected]"
export GIT_COMMITTER_NAME="Travis CI"
TEMPORARY_REPOSITORY=$(mktemp -d)
git clone "https://github.com/$GITHUB_REPO" "$TEMPORARY_REPOSITORY"
cd $TEMPORARY_REPOSITORY
echo "Checking out $SOURCE_BRANCH"
git checkout $SOURCE_BRANCH
echo "Checking out $MERGE_BRANCH"
git checkout $MERGE_BRANCH
echo "Merging $SOURCE_BRANCH into $MERGE_BRANCH"
git merge --ff-only "$SOURCE_BRANCH"
echo "Pushing to $GITHUB_REPO"
# Redirect to /dev/null to avoid secret leakage
git push "https://$GITHUB_SECRET_TOKEN@github.com/$GITHUB_REPO" $MERGE_BRANCH > /dev/null 2>&1
That script makes sure that the build is on dev, and that it is not a pull request build. It will then merge dev to master locally and then push it to the github repo.
To make sure it runs only when the build is successful, wrap in a gate script that runs it only accordingly: .travis/merge_dev_to_master_gate.sh
#!/usr/bin/env sh
if [ $TRAVIS_TEST_RESULT -eq 0 ];
then
.travis/merge-dev-to-master.sh
RETURN_VALUE=$?
if [ "$RETURN_VALUE" != "0" ];
then
exit $RETURN_VALUE
fi
fi
Release builds need to be run manually only. That involves slight modifications to the scripts, as well as a new script that will call the Travis API v3 to trigger a build.
In .travis/build.sh
, add the following prior to the gradle clean build
calls, to make sure the version of the project does not contain any -SNAPSHOT
if [ "$MANUAL_RELEASE_TRIGGERED" = "true" ];
then
# This is a release job, triggered manually
# Change the version locally to remove the -SNAPSHOT
sed -i "s/-SNAPSHOT//g" gradle.properties
echo "This is a manual release!"
else
echo "Not a manual release"
fi
In the end, build.sh
should look like this:
#!/usr/bin/env sh
chmod u+x gradlew
if [ "$MANUAL_RELEASE_TRIGGERED" = "true" ];
then
# This is a release job, triggered manually
# Change the version locally to remove the -SNAPSHOT
sed -i "s/-SNAPSHOT//g" gradle.properties
echo "This is a manual release!"
else
echo "Not a manual release"
fi
if [ "$TRAVIS_PULL_REQUEST" != "false" ];
then
echo "Skip integration tests in pull request builds"
./gradlew clean build -x integrationTest
else
echo "Temporarily skip integration tests in all builds. Too heavy for Travis"
./gradlew clean build -x integrationTest
fi
Add a deploy.sh
script to the .travis
folder:
#!/usr/bin/env sh
if [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ];
then
if [ "$MANUAL_RELEASE_TRIGGERED" = "true" ];
then
echo "Sign, Upload archives to local repo, Upload archives to Sonatype, Close and release repository."
./gradlew uploadArchives publishToNexusAndClose
fi
fi
To make sure it runs only when the build is successful, wrap in a gate script that runs it only accordingly:
#!/usr/bin/env sh
if [ $TRAVIS_TEST_RESULT -eq 0 ];
then
.travis/deploy.sh
RETURN_VALUE=$?
if [ "$RETURN_VALUE" != "0" ];
then
exit $RETURN_VALUE
fi
fi
This is deprecated and stopped working: https://github.com/travis-ci/travis-ci/issues/9555. See next section for a workaround.
Once an artifact is uploaded as a release to Sonatype, it needs to undergo some verifications while being promoted. There is a gradle plugin to do that.
in build.gradle
add:
plugins
{
id "io.codearte.nexus-staging" version "0.8.0"
}
After the gradle calls in deploy.sh
, add the following:
if [ "$MANUAL_RELEASE_TRIGGERED" = "true" ];
then
echo "Promote repository"
./gradlew closeAndReleaseRepository
fi
This will call the promote plugin to automatically promote the release.
In the end, deploy.sh
should look like this:
#!/usr/bin/env sh
if [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ];
then
echo "Sign Archives"
./gradlew signArchives
echo "Upload Archives"
./gradlew uploadArchives
if [ "$MANUAL_RELEASE_TRIGGERED" = "true" ];
then
echo "Promote repository"
./gradlew closeAndReleaseRepository
fi
fi
Due to a Travis limitation (https://github.com/travis-ci/travis-ci/issues/9555), and until a better solution is available, add this to the build.gradle
:
apply plugin: 'maven-publish'
def uploadAndRelease(def username, def password, def repoDir) {
def proc = ['./uploadAndRelease.sh', username, password, repoDir].execute([], file("${rootDir.toString()}/gradle"))
proc.waitForProcessOutput(System.out, System.err)
}
task publishToNexusAndClose(dependsOn: 'publish'){
doLast {
uploadAndRelease(System.getenv('SONATYPE_USERNAME'), System.getenv('SONATYPE_PASSWORD'), "$rootDir/build/deploy")
}
}
For this to work, the <Project>/gradle
folder needs to contain the following scripts, which talk to oss.sonatype.org directly to upload the artifacts:
uploadAndRelease.sh
:
#!/usr/bin/env bash
export SONATYPE_USERNAME=$1
export SONATYPE_PASSWORD=$2
export REPOSITORY_DIR=$3
export API_ENDPOINT=https://oss.sonatype.org/service/local
function runWithRetry()
{
n=0
until [ $n -ge 100 ]
do
$1 && break
n=$[$n+1]
echo "Sleep 15 sec before retry"
sleep 15
echo "Retry $n"
done
}
export ATLAS_PROFILE_ID=1442a4f451744
export DESCRIPTION_PAYLOAD="<promoteRequest>\
<data>\
<description>Atlas Release</description>\
</data>\
</promoteRequest>"
# Create staging repo
export STAGING_ID=$(curl -s -u $SONATYPE_USERNAME:$SONATYPE_PASSWORD \
-X POST \
-H "Content-Type:application/xml" \
-d "$DESCRIPTION_PAYLOAD" \
"$API_ENDPOINT/staging/profiles/$ATLAS_PROFILE_ID/start" \
| perl -nle 'print "$1" if ($_ =~ /.*<stagedRepositoryId>(.*)<\/stagedRepositoryId>.*/g);' \
| awk '{$1=$1};1')
# Response parsed looks like this:
# <promoteResponse> <data> <stagedRepositoryId>orgopenstreetmapatlas-1147</stagedRepositoryId> <description>Atlas Release</description> </data></promoteResponse>
export CLOSE_PAYLOAD="<promoteRequest>\
<data>\
<stagedRepositoryId>$STAGING_ID</stagedRepositoryId>\
<description>Close Atlas repo</description>\
</data>\
</promoteRequest>"
# Upload
./uploadToNexus.sh $SONATYPE_USERNAME $SONATYPE_PASSWORD $REPOSITORY_DIR $STAGING_ID
echo "sleep 20 seconds before closing"
sleep 20
# Close
curl --fail -s -u $SONATYPE_USERNAME:$SONATYPE_PASSWORD \
-X POST \
-H "Content-Type:application/xml" \
-d "$CLOSE_PAYLOAD" \
"$API_ENDPOINT/staging/profiles/$ATLAS_PROFILE_ID/finish"
echo "sleep 120 before releasing (to let validation happen)"
sleep 120
# Release
runWithRetry ./releaseSonatype.sh
# Drop if needed. It is usually automatically cleaned up.
# runWithRetry ./dropSonatype.sh
uploadToNexus.sh
:
#!/usr/bin/env bash
name=$1
password=$2
repodir=$3
stagingId=$4
find $repodir -type f | while read f; do
suffix=$(echo $f | sed "s%^$repodir/%%")
echo "Uploading to: ${stagingId}: ${suffix}"
curl -s -u $name:$password -H "Content-type: application/x-rpm" --upload-file $f https://oss.sonatype.org/service/local/staging/deployByRepositoryId/${stagingId}/${suffix}
done
releaseSonatype.sh
export RELEASE_PAYLOAD='{'\
' "data":{'\
' "stagedRepositoryIds":['\
' "'$STAGING_ID'"'\
' ],'\
' "description":"Releasing Atlas repo"'\
' }'\
'}'
curl --fail -s -u $SONATYPE_USERNAME:$SONATYPE_PASSWORD \
-X POST \
-H "Content-Type:application/json" \
-d "$RELEASE_PAYLOAD" \
"$API_ENDPOINT/staging/bulk/promote" \
dropSonatype.sh
export DROP_PAYLOAD='{'\
' "data":{'\
' "stagedRepositoryIds":['\
' "'$STAGING_ID'"'\
' ],'\
' "description":"Dropping Atlas repo"'\
' }'\
'}'
curl --fail -u $SONATYPE_USERNAME:$SONATYPE_PASSWORD \
-X POST \
-H "Content-Type:application/json" \
-d "$DROP_PAYLOAD" \
"$API_ENDPOINT/staging/bulk/drop"
In .travis
, add a tag-master.sh
script that will mark the tag from master
, if the release succeeded. Make sure to update the GITHUB_REPO
variable.
#!/usr/bin/env sh
GITHUB_REPO="osmlab/atlas"
RELEASE_BRANCH=master
FUNCTION_NAME="tag-$RELEASE_BRANCH"
echo "$FUNCTION_NAME: $GITHUB_REPO"
echo "$FUNCTION_NAME: TRAVIS_BRANCH = $TRAVIS_BRANCH"
echo "$FUNCTION_NAME: TRAVIS_PULL_REQUEST = $TRAVIS_PULL_REQUEST"
if [ "$TRAVIS_BRANCH" != "$RELEASE_BRANCH" ];
then
echo "$FUNCTION_NAME: Exiting! Branch is not $RELEASE_BRANCH: ($TRAVIS_BRANCH)"
exit 0;
fi
if [ "$TRAVIS_PULL_REQUEST" != "false" ];
then
echo "$FUNCTION_NAME: Exiting! This is a Pull Request: $TRAVIS_PULL_REQUEST"
exit 0;
fi
if [ "$MANUAL_RELEASE_TRIGGERED" != "true" ];
then
echo "$FUNCTION_NAME: Exiting! This is not a release build."
exit 0;
fi
: ${GITHUB_SECRET_TOKEN:?"GITHUB_SECRET_TOKEN needs to be set in .travis.yml!"}
export GIT_COMMITTER_EMAIL="[email protected]"
export GIT_COMMITTER_NAME="Travis CI"
TEMPORARY_REPOSITORY=$(mktemp -d)
git clone "https://github.com/$GITHUB_REPO" "$TEMPORARY_REPOSITORY"
cd $TEMPORARY_REPOSITORY
echo "Checking out $RELEASE_BRANCH"
git checkout $RELEASE_BRANCH
PROJECT_VERSION=$(cat gradle.properties | grep "\-SNAPSHOT" | awk -F '=' '{print $2}' | awk -F '-' '{print $1}')
: ${PROJECT_VERSION:?"PROJECT_VERSION could not be found."}
echo "Tagging $RELEASE_BRANCH at version $PROJECT_VERSION"
git tag -a $PROJECT_VERSION -m "Release $PROJECT_VERSION"
echo "Pushing tag $PROJECT_VERSION to $GITHUB_REPO"
# Redirect to /dev/null to avoid secret leakage
git push "https://$GITHUB_SECRET_TOKEN@github.com/$GITHUB_REPO" $PROJECT_VERSION > /dev/null 2>&1
To make sure it runs only when the build is successful, wrap in a gate script tag-master-gate.sh
that runs it only accordingly:
#!/usr/bin/env sh
if [ $TRAVIS_TEST_RESULT -eq 0 ];
then
.travis/tag-master.sh
RETURN_VALUE=$?
if [ "$RETURN_VALUE" != "0" ];
then
exit $RETURN_VALUE
fi
fi
In .travis/
, add a script trigger-release.sh
that can trigger builds. Make sure to update the GITHUB_ORGANIZATION
and GITHUB_REPOSITORY_NAME
below:
#!/usr/bin/env sh
# Use Travis to trigger a release from Master
GITHUB_ORGANIZATION=osmlab
GITHUB_REPOSITORY_NAME=atlas
# Assumptions
# - This is called from the root of the project
# - The travis client is installed: gem install travis
# - travis login --org has been called to authenticate
TRAVIS_PERSONAL_TOKEN=$(travis token)
:${TRAVIS_PERSONAL_TOKEN:?"TRAVIS_PERSONAL_TOKEN needs to be set to access the Travis API to trigger the build"}
body='
{
"request":
{
"branch": "master",
"config":
{
"before_script": "export MANUAL_RELEASE_TRIGGERED=true"
}
}
}'
curl -s -X POST \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-H "Travis-API-Version: 3" \
-H "Authorization: token $TRAVIS_PERSONAL_TOKEN" \
-d "$body" \
https://api.travis-ci.org/repo/$GITHUB_ORGANIZATION%2F$GITHUB_REPOSITORY_NAME/requests
Here the "manual" part is tracked with an environment variable called MANUAL_RELEASE_TRIGGERED
.
Call that script from the root of the repo, manually, and after travis login
has been called.