Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ci): Automate Docker image publication when merging on master #1142

Merged
merged 4 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could just be:

Suggested change
"publish:github": "./scripts/publish-github",
"publish:github": "semantic-release",

and the file removed

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. I initially had the external script because there was a bit more code before and after, but you're right that it can now be simplified

"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