• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

screwdriver-cd / screwdriver / #3202

25 Jul 2025 04:52PM UTC coverage: 67.669% (-27.3%) from 94.935%
#3202

push

screwdriver

web-flow
feat(3363): Update the existing endpoint to get admin for a pipeline from the specified SCM context (#3370)

1284 of 2114 branches covered (60.74%)

Branch coverage included in aggregate %.

1 of 11 new or added lines in 1 file covered. (9.09%)

1235 existing lines in 49 files now uncovered.

3417 of 4833 relevant lines covered (70.7%)

50.53 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

13.11
/plugins/pipelines/create.js
1
'use strict';
2

3
const urlLib = require('url');
1✔
4
const boom = require('@hapi/boom');
1✔
5
const schema = require('screwdriver-data-schema');
1✔
6
const logger = require('screwdriver-logger');
1✔
7
const { formatCheckoutUrl, sanitizeRootDir } = require('./helper');
1✔
8
const { getUserPermissions } = require('../helper');
1✔
9
const ANNOTATION_USE_DEPLOY_KEY = 'screwdriver.cd/useDeployKey';
1✔
10

11
module.exports = () => ({
434✔
12
    method: 'POST',
13
    path: '/pipelines',
14
    options: {
15
        description: 'Create a new pipeline',
16
        notes: 'Create a specific pipeline',
17
        tags: ['api', 'pipelines'],
18
        auth: {
19
            strategies: ['token'],
20
            scope: ['user', '!guest']
21
        },
22

23
        handler: async (request, h) => {
UNCOV
24
            const checkoutUrl = formatCheckoutUrl(request.payload.checkoutUrl);
×
UNCOV
25
            const rootDir = sanitizeRootDir(request.payload.rootDir);
×
UNCOV
26
            const { autoKeysGeneration } = request.payload;
×
UNCOV
27
            const { pipelineFactory, userFactory, collectionFactory, secretFactory } = request.server.app;
×
UNCOV
28
            const { username, scmContext } = request.auth.credentials;
×
UNCOV
29
            const deployKeySecret = 'SD_SCM_DEPLOY_KEY';
×
30

31
            // fetch the user
UNCOV
32
            const user = await userFactory.get({ username, scmContext });
×
UNCOV
33
            const token = await user.unsealToken();
×
34

35
            let scmUri;
36

UNCOV
37
            try {
×
UNCOV
38
                scmUri = await pipelineFactory.scm.parseUrl({
×
39
                    scmContext,
40
                    rootDir,
41
                    checkoutUrl,
42
                    token
43
                });
44
            } catch (error) {
UNCOV
45
                logger.error(error.message);
×
UNCOV
46
                throw boom.boomify(error, { statusCode: error.statusCode });
×
47
            }
48

49
            // get the user permissions for the repo
UNCOV
50
            await getUserPermissions({ user, scmUri });
×
51

52
            // see if there is already a pipeline
UNCOV
53
            let pipeline = await pipelineFactory.get({ scmUri });
×
54

55
            // if there is already a pipeline for the checkoutUrl, reject
UNCOV
56
            if (pipeline) {
×
UNCOV
57
                throw boom.conflict(`Pipeline already exists with the ID: ${pipeline.id}`, {
×
58
                    existingId: pipeline.id
59
                });
60
            }
61
            // set up pipeline admins, and create a new pipeline
UNCOV
62
            const pipelineConfig = {
×
63
                admins: {
64
                    [username]: true
65
                },
66
                scmContext,
67
                scmUri
68
            };
69

UNCOV
70
            logger.info(`[Audit] user ${user.username}:${scmContext} creates the pipeline for ${scmUri}.`);
×
UNCOV
71
            pipeline = await pipelineFactory.create(pipelineConfig);
×
72

UNCOV
73
            const collections = await collectionFactory.list({
×
74
                params: {
75
                    userId: user.id,
76
                    type: 'default'
77
                }
78
            });
79
            let defaultCollection;
80

UNCOV
81
            if (collections && collections.length > 0) {
×
UNCOV
82
                [defaultCollection] = collections;
×
83
            }
84

UNCOV
85
            if (!defaultCollection) {
×
86
                defaultCollection = await collectionFactory.create({
×
87
                    userId: user.id,
88
                    name: 'My Pipelines',
89
                    description: `The default collection for ${user.username}`,
90
                    type: 'default'
91
                });
92
            }
93

94
            // Check if the pipeline exists in the default collection
95
            // to prevent the situation where a pipeline is deleted and then created right away with the same id
UNCOV
96
            if (!defaultCollection.pipelineIds.includes(pipeline.id)) {
×
97
                Object.assign(defaultCollection, {
×
98
                    pipelineIds: [...defaultCollection.pipelineIds, pipeline.id]
99
                });
100

101
                await defaultCollection.update();
×
102
            }
103

UNCOV
104
            const results = await pipeline.sync();
×
105

106
            // check if pipeline has deploy key annotation then create secrets
UNCOV
107
            const deployKeyAnnotation = pipeline.annotations && pipeline.annotations[ANNOTATION_USE_DEPLOY_KEY];
×
108

UNCOV
109
            if (autoKeysGeneration || deployKeyAnnotation) {
×
UNCOV
110
                const privateDeployKey = await pipelineFactory.scm.addDeployKey({
×
111
                    scmContext,
112
                    checkoutUrl,
113
                    token
114
                });
UNCOV
115
                const privateDeployKeyB64 = Buffer.from(privateDeployKey).toString('base64');
×
116

UNCOV
117
                await secretFactory.create({
×
118
                    pipelineId: pipeline.id,
119
                    name: deployKeySecret,
120
                    value: privateDeployKeyB64,
121
                    allowInPR: true
122
                });
123
            }
124

UNCOV
125
            await pipeline.addWebhooks(`${request.server.info.uri}/v4/webhooks`);
×
126

UNCOV
127
            const location = urlLib.format({
×
128
                host: request.headers.host,
129
                port: request.headers.port,
130
                protocol: request.server.info.protocol,
131
                pathname: `${request.path}/${pipeline.id}`
132
            });
UNCOV
133
            const data = await results.toJson();
×
134

UNCOV
135
            return h.response(data).header('Location', location).code(201);
×
136
        },
137
        validate: {
138
            payload: schema.models.pipeline.create
139
        }
140
    }
141
});
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc