Skip to content

Commit

Permalink
Merge pull request #75 from dependabot/brrygrdn/one-way-to-get-images
Browse files Browse the repository at this point in the history
Use the ImageService to fetch dependencies in development and CI
  • Loading branch information
Barry Gordon authored and GitHub committed Feb 15, 2022
2 parents c46e41a + 88dd91b commit 9e92ed4
Show file tree
Hide file tree
Showing 16 changed files with 174 additions and 39 deletions.
16 changes: 7 additions & 9 deletions .github/workflows/integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,6 @@ jobs:
integration:
runs-on: ubuntu-latest
steps:
- name: GPR login
run: docker login docker.pkg.github.com -u x -p ${{ secrets.GITHUB_TOKEN }}

- name: GRP pull dependabot/dependabot-updater
run: docker pull docker.pkg.github.com/dependabot/dependabot-updater:v1

- name: GRP pull github/dependabot-update-job-proxy
run: docker pull docker.pkg.github.com/github/dependabot-update-job-proxy:v1

- uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.sha }}
Expand All @@ -38,5 +29,12 @@ jobs:
- name: Install NPM dependencies
run: npm ci

- name: Pre-fetch the pinned images
run: npm run fetch-images
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Run integration tests
run: npm run test-integration
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ typings/
.env
.env.test

# asdf tool version
.tool-versions

# parcel-bundler cache (https://parceljs.org/)
.cache

Expand Down
7 changes: 5 additions & 2 deletions __tests__/container-service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ describe('ContainerService', () => {
const docker = new Docker()
let container: any

beforeAll(async () => {
/* We use alpine as a small, easy-to-script-for test stand-in for the updater */
await ImageService.fetchImage('alpine')
})

describe('when a container runs successfully', () => {
beforeEach(async () => {
await ImageService.pull('alpine')
container = await docker.createContainer({
Image: 'alpine',
AttachStdout: true,
Expand All @@ -26,7 +30,6 @@ describe('ContainerService', () => {

describe('when a container runs unsuccessfully', () => {
beforeEach(async () => {
await ImageService.pull('alpine')
container = await docker.createContainer({
Image: 'alpine',
AttachStdout: true,
Expand Down
15 changes: 15 additions & 0 deletions __tests__/docker-tags.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {UPDATER_IMAGE_NAME, PROXY_IMAGE_NAME} from '../src/docker-tags'

describe('Docker tags', () => {
test('UPDATER_IMAGE_NAME', () => {
expect(UPDATER_IMAGE_NAME).toMatch(
/^docker\.pkg\.github\.com\/dependabot\/dependabot-updater:v1$/
)
})

test('PROXY_IMAGE_NAME', () => {
expect(PROXY_IMAGE_NAME).toMatch(
/^docker\.pkg\.github\.com\/github\/dependabot-update-job-proxy:v1$/
)
})
})
2 changes: 1 addition & 1 deletion __tests__/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Docker from 'dockerode'
import {UPDATER_IMAGE_NAME, PROXY_IMAGE_NAME} from '../src/main'
import {UPDATER_IMAGE_NAME, PROXY_IMAGE_NAME} from '../src/docker-tags'
import waitPort from 'wait-port'
import path from 'path'
import {spawn} from 'child_process'
Expand Down
46 changes: 44 additions & 2 deletions __tests__/image-service.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,49 @@
import {ImageService} from '../src/image-service'

describe('ImageService', () => {
test('pulls the image from docker hub', async () => {
await ImageService.pull('hello-world')
const originalEnv = process.env

describe('when GITHUB_TOKEN is not set', () => {
beforeEach(async () => {
jest.resetModules()
process.env = {
...originalEnv,
GITHUB_TOKEN: undefined
}
})

afterEach(async () => {
process.env = originalEnv
})

test('it raises an error', async () => {
await expect(
ImageService.pull('ghcr.io/dependabot/dependabot-core:latest')
).rejects.toThrowError(
new Error('No GITHUB_TOKEN set, unable to pull images.')
)
})
})

describe('when asked to fetch non-GitHub hosted images', () => {
beforeEach(async () => {
jest.resetModules()
process.env = {
...originalEnv,
GITHUB_TOKEN: 'mock_token'
}
})

afterEach(async () => {
process.env = originalEnv
})

test('it raises an error', async () => {
await expect(ImageService.pull('hello-world')).rejects.toThrowError(
new Error(
'Only images distributed via docker.pkg.github.com or ghcr.io can be fetched'
)
)
})
})
})
2 changes: 1 addition & 1 deletion __tests__/proxy-integration.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Docker from 'dockerode'
import {Credential} from '../src/api-client'
import {ImageService} from '../src/image-service'
import {PROXY_IMAGE_NAME} from '../src/main'
import {PROXY_IMAGE_NAME} from '../src/docker-tags'
import {ProxyBuilder} from '../src/proxy'
import {integration, removeDanglingUpdaterContainers} from './helpers'
import {spawnSync} from 'child_process'
Expand Down
2 changes: 1 addition & 1 deletion __tests__/updater-builder-integration.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {PROXY_IMAGE_NAME, UPDATER_IMAGE_NAME} from '../src/main'
import {UPDATER_IMAGE_NAME, PROXY_IMAGE_NAME} from '../src/docker-tags'
import {ImageService} from '../src/image-service'
import {removeDanglingUpdaterContainers, integration} from './helpers'
import Docker from 'dockerode'
Expand Down
2 changes: 1 addition & 1 deletion __tests__/updater-integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import path from 'path'
import {ApiClient} from '../src/api-client'
import {ImageService} from '../src/image-service'
import {JobParameters} from '../src/inputs'
import {UPDATER_IMAGE_NAME, PROXY_IMAGE_NAME} from '../src/main'
import {UPDATER_IMAGE_NAME, PROXY_IMAGE_NAME} from '../src/docker-tags'
import {Updater} from '../src/updater'

import {
Expand Down
53 changes: 43 additions & 10 deletions dist/main/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/main/index.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
"test": "SKIP_INTEGRATION_TESTS=true jest --detectOpenHandles",
"test-integration": "jest --detectOpenHandles 'integration'",
"prepare": "husky install",
"all": "npm run format && npm run lint && npm run package && npm test",
"dependabot": "npx ts-node src/cli.ts"
"dependabot": "ts-node src/cli.ts",
"fetch-images": "ts-node src/fetch-images.ts"
},
"repository": {
"type": "git",
Expand Down
4 changes: 4 additions & 0 deletions src/docker-tags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const UPDATER_IMAGE_NAME =
'docker.pkg.github.com/dependabot/dependabot-updater:v1'
export const PROXY_IMAGE_NAME =
'docker.pkg.github.com/github/dependabot-update-job-proxy:v1'
9 changes: 9 additions & 0 deletions src/fetch-images.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {ImageService} from './image-service'
import {UPDATER_IMAGE_NAME, PROXY_IMAGE_NAME} from './docker-tags'

export async function run(): Promise<void> {
await ImageService.pull(UPDATER_IMAGE_NAME)
await ImageService.pull(PROXY_IMAGE_NAME)
}

run()
36 changes: 34 additions & 2 deletions src/image-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,32 @@ const endOfStream = async (docker: Docker, stream: Readable): Promise<void> => {
})
}

/** Fetch the configured updater image, if it isn't already available. */
export const ImageService = {
/** Fetch the configured updater image, if it isn't already available. */
async pull(imageName: string, force = false): Promise<void> {
/*
This method fetches images using a GITHUB_TOKEN we should check two things:
- The process has a GITHUB_TOKEN set so we don't attempt a failed call to docker
- The image being requested is actually hosted on GitHub.
We expose the `fetch_image` utility method to allow us to pull in arbitrary images
without auth in unit tests.
*/
if (
!(
imageName.startsWith('ghcr.io/') ||
imageName.startsWith('docker.pkg.github.com/')
)
) {
throw new Error(
'Only images distributed via docker.pkg.github.com or ghcr.io can be fetched'
)
}

if (!process.env.GITHUB_TOKEN) {
throw new Error('No GITHUB_TOKEN set, unable to pull images.')
}

const docker = new Docker()
try {
const image = await docker.getImage(imageName).inspect()
Expand All @@ -26,11 +49,20 @@ export const ImageService = {
} // else fallthrough to pull
}

core.info(`Pulling image ${imageName}...`)
const auth = {
username: 'x',
password: process.env.GITHUB_TOKEN
}
this.fetchImage(imageName, auth, docker)
},

/* Retrieve the imageName using the auth details provided, if any */
async fetchImage(
imageName: string,
auth = {},
docker = new Docker()
): Promise<void> {
core.info(`Pulling image ${imageName}...`)
const stream = await docker.pull(imageName, {authconfig: auth})
await endOfStream(docker, stream)
core.info(`Pulled image ${imageName}`)
Expand Down
10 changes: 3 additions & 7 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import axios from 'axios'
import * as core from '@actions/core'
import * as github from '@actions/github'
import {Context} from '@actions/github/lib/context'
import {ApiClient, CredentialFetchingError} from './api-client'
import {getJobParameters} from './inputs'
import {ImageService} from './image-service'
import {UPDATER_IMAGE_NAME, PROXY_IMAGE_NAME} from './docker-tags'
import {Updater, UpdaterFetchError} from './updater'
import {ApiClient, CredentialFetchingError} from './api-client'
import axios from 'axios'

export const UPDATER_IMAGE_NAME =
'docker.pkg.github.com/dependabot/dependabot-updater:v1'
export const PROXY_IMAGE_NAME =
'docker.pkg.github.com/github/dependabot-update-job-proxy:v1'

export enum DependabotErrorType {
Unknown = 'actions_workflow_unknown',
Expand Down

0 comments on commit 9e92ed4

Please sign in to comment.