Skip to content

Commit

Permalink
feat(ci): Automate Docker image publication when merging on master (#…
Browse files Browse the repository at this point in the history
…1142)

* feat(ci): Publish Docker image to GitHub Packages from CI

* chore(ci): Cleanup the Docker publish script

* chore(ci): Remove reference to CircleCI

* fix(ci): Remove useless config and dependency
  • Loading branch information
pixelastic committed Oct 20, 2023
1 parent fd31d88 commit f0208ba
Show file tree
Hide file tree
Showing 11 changed files with 205 additions and 107 deletions.
80 changes: 0 additions & 80 deletions .circleci/config.yml

This file was deleted.

1 change: 0 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ cypress/
.git/
.github/
.githooks/
.circleci/
.husky/
.nodemon.json
.editorconfig
Expand Down
49 changes: 49 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Publish
run-name: 🚀 Commit on ${{ github.ref_name }}

on:
push:
branches:
- master

jobs:
validate:
name: 🤖 Validate
uses: ./.github/workflows/validate.yml
publish:
name: 📦 Publish
needs: validate
runs-on: ubuntu-latest
permissions:
# To commit on the repo and add the tag
contents: write
# To publish the Docker image to GitHub Packages
packages: write
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

steps:
- name: 📁 Checkout code
uses: actions/checkout@v4

- name: ⚙️ Setup node
uses: actions/setup-node@v3
with:
node-version: '18.18'
cache: 'yarn'

- name: 📦 Install dependencies
run: yarn install --frozen-lockfile

- name: ❓ Check if a new version needs to be published
id: publish-check
continue-on-error: true
run: yarn publish:check

- name: 🆙 Bump version, tag commit, publish GitHub Release
if: ${{ steps.publish-check.outcome == 'success' }}
run: yarn publish:github

- name: 🐋 Publish Docker image
if: ${{ steps.publish-check.outcome == 'success' }}
run: yarn publish:docker
5 changes: 4 additions & 1 deletion .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ name: Validate
run-name: 🤖 Validating code on ${{ github.ref_name }}

on:
push:
pull_request:
# on.workflow_call is required for this workflow to be re-used
# See: https://docs.github.com/en/actions/using-workflows/reusing-workflows#creating-a-reusable-workflow
workflow_call:

jobs:
validate:
Expand Down
14 changes: 0 additions & 14 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,4 @@ module.exports = {

testEnvironment: 'node',
modulePaths: ['src'],

// reporter for circleci
reporters: [
'default',
[
'jest-junit',
{
outputDirectory: 'junit',
suiteNameTemplate: '{filepath}',
ancestorSeparator: ' › ',
addFileAttribute: 'true',
},
],
],
};
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
"start_new": "indexName=npm-search-new bootstrapIndexName=npm-search-new.tmp UV_THREADPOOL_SIZE=64 node --max-old-space-size=1500 dist/index.js",
"test:watch": "jest --watchAll --no-watchman",
"test": "jest --forceExit",
"docker:build": "./scripts/build.sh",
"docker:release": "VERSION=$(node -e \"console.log(require('./package.json').version)\") && echo \"Releasing $VERSION\" && docker push algolia/npm-search && docker push algolia/npm-search:$VERSION"
"publish:check": "node ./scripts/publish-check.js",
"publish:github": "./scripts/publish-github",
"publish:docker": "./scripts/publish-docker"
},
"license": "MIT",
"dependencies": {
Expand Down Expand Up @@ -50,7 +51,6 @@
"devDependencies": {
"@semantic-release/changelog": "6.0.1",
"@semantic-release/git": "10.0.1",
"@semantic-release/npm": "9.0.1",
"@types/escape-html": "1.0.2",
"@types/hosted-git-info": "3.0.2",
"@types/jest": "28.1.7",
Expand Down
56 changes: 50 additions & 6 deletions release.config.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,57 @@
/* eslint-disable import/no-commonjs */
/* eslint-disable no-template-curly-in-string */
/**
* We use semantic-release to automate the publishing of new versions based on
* the commit history: whenever a commit is pushed to the master branch, it
* checks if any commit had a BREAKING CHANGE / feat() / fix() message, and
* publishes (or not) a new major.minor/patch version accordingly.
*
* See: https://github.com/semantic-release/semantic-release.
*
* Semantic-release executes steps in order (from verifyConditions to
* success/fail). For each step, it execute the matching code in each plugin (if
* such exists). If any step fails, the whole process stop.
*
* As we are using a mix of core and community plugins, as well as slightly
* diverging from the default use-case, we explictly define the order of plugins
* in each step instead of relying on the default order.
*
* The current configuration will:
* - Check if a new version needs to be published (and stop if not)
* - Update the version number in package.json accordingly
* - Update the CHANGELOG.md with the changes
* - Create a new commit, and tag it with the version number
* - Publish the code source to GitHub Releases (not very useful).
*
* Specifically, it does not:
* - Publish the code to npm (this is not an npm module)
* - Publish the Docker image (yarn publish:docker takes care of that).
**/
module.exports = {
branches: 'master',
verifyConditions: ['@semantic-release/github'],
plugins: [
// Those 4 plugins are part of the core of semantic-release
'@semantic-release/commit-analyzer',
'@semantic-release/release-notes-generator',
'@semantic-release/npm',
'@semantic-release/github',
// Those 2 are additional plugins
'@semantic-release/changelog',
'@semantic-release/git',
],
// Below are the various steps
// Source: https://semantic-release.gitbook.io/semantic-release/usage/plugins
// We explicitly define because it allows us to:
// - remove steps that we don't need (for example verifying npm credentials as
// we don't publish on npm)
// - put steps in order (for example updating the changelog file before
// committing it)
verifyConditions: ['@semantic-release/github', '@semantic-release/git'],
analyzeCommits: ['@semantic-release/commit-analyzer'],
verifyRelease: [],
generateNotes: ['@semantic-release/release-notes-generator'],
prepare: [
{
path: '@semantic-release/changelog',
changelogFile: 'CHANGELOG.md',
},
'@semantic-release/changelog',
'@semantic-release/npm',
{
path: '@semantic-release/git',
Expand All @@ -17,7 +61,7 @@ module.exports = {
},
],
publish: ['@semantic-release/github'],
addChannel: [],
success: [],
fail: [],
npmPublish: false,
};
50 changes: 50 additions & 0 deletions scripts/publish-check.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/* eslint-disable import/no-commonjs */
/* eslint-disable no-console */
/* eslint-disable no-process-exit */
/* eslint-disable @typescript-eslint/no-var-requires */
const { Writable } = require('node:stream');

const semanticRelease = require('semantic-release');

(async () => {
console.log('Analyzing commits since last version...');

const stream = new Writable({
write(_chunk, _encoding, callback) {
setImmediate(callback);
},
});

// Execute semantic-release with only the commit-analyzer step, to see if
// a new release is needed
const { nextRelease } = await semanticRelease(
{
dryRun: true,
plugins: ['@semantic-release/commit-analyzer'],
verifyConditions: [],
analyzeCommits: ['@semantic-release/commit-analyzer'],
verifyRelease: [],
generateNotes: [],
prepare: [],
publish: [],
addChannel: [],
success: [],
fail: [],
},
// Redirect output to new streams, to make the script silent
{
stdout: stream,
stderr: stream,
}
);

// Exit with 0 if a new version must be released, 1 if nothing to do
if (nextRelease?.version) {
console.log(
`Commits analyzed warrant a release of version ${nextRelease.version}`
);
process.exit(0);
}
console.log('No new version to publish');
process.exit(1);
})();
38 changes: 38 additions & 0 deletions scripts/publish-docker
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/bin/sh
# Publish the project on GitHub Packages
# See: https://github.com/algolia/npm-search/pkgs/container/npm-search
#
# This script will be automatically run from GitHub Actions on each commits on
# the main branch that warrants a release (ie. feat() and fix() commits).
#
# You can also run the script locally, but you'll need a GITHUB_TOKEN with the
# write:packages scope.
# See: https://github.com/settings/tokens
set -e

# Get version from package.json
version=$(node -e "console.log(require('./package.json').version)")
echo "Publishing: $version"
echo ""

# Build the image
docker build \
--platform linux/amd64 \
--label "org.opencontainers.image.source=https://github.com/algolia/npm-search" \
--tag "ghcr.io/algolia/npm-search" \
--tag "ghcr.io/algolia/npm-search:${version}" \
.

# Login to ghcr.io
echo "${GITHUB_TOKEN}" |
docker login ghcr.io \
--username $ \
--password-stdin

# Push the image
docker push "ghcr.io/algolia/npm-search"
docker push "ghcr.io/algolia/npm-search:${version}"

# Output
echo "Version $version published"
echo "https://github.com/algolia/npm-search/pkgs/container/npm-search"
10 changes: 10 additions & 0 deletions scripts/publish-github
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/sh
# Publish a new version on GitHub, including:
# - Update package.json and CHANGELOG.md with new version and changes
# - Tag the commit with the version number
# - Release the source code on GitHub Releases (https://github.com/algolia/npm-search/releases)
#
# This script doesn't do anything if there is no new version to publish
set -e

yarn run semantic-release
3 changes: 1 addition & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1434,7 +1434,7 @@ __metadata:
languageName: node
linkType: hard

"@semantic-release/npm@npm:9.0.1, @semantic-release/npm@npm:^9.0.0":
"@semantic-release/npm@npm:^9.0.0":
version: 9.0.1
resolution: "@semantic-release/npm@npm:9.0.1"
dependencies:
Expand Down Expand Up @@ -7415,7 +7415,6 @@ __metadata:
"@algolia/requester-node-http": 4.14.2
"@semantic-release/changelog": 6.0.1
"@semantic-release/git": 10.0.1
"@semantic-release/npm": 9.0.1
"@types/bluebird": ^3.5.39
"@types/escape-html": 1.0.2
"@types/hosted-git-info": 3.0.2
Expand Down

0 comments on commit f0208ba

Please sign in to comment.