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

refactor setup script #13323

Merged
merged 18 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from 12 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
6 changes: 3 additions & 3 deletions .github/workflows/run-playwright-on-pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@ jobs:
with:
fetch-depth: 0

- name: 'Installing Dependencies'
uses: ./.github/actions/yarn-install

- name: Generate .env file
run: |
echo PLAYWRIGHT_TEST_APP=autodeploy-v3 >> .env
Expand All @@ -55,6 +52,9 @@ jobs:
run: |
node ./development/setup.js

- name: 'Installing Dependencies'
uses: ./.github/actions/yarn-install

- name: Playwright run
working-directory: frontend/testing/playwright
env:
Expand Down
6 changes: 4 additions & 2 deletions compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ services:
- OidcLoginSettings:Authority=http://studio.localhost/repos/
- OidcLoginSettings:RequireHttpsMetadata=false
- OidcLoginSettings:CookieExpiryTimeInMinutes=59
- OidcLoginSettings:FetchClientIdAndSecretFromRootEnvFile=false
ports:
- '6000:6000'
depends_on:
Expand Down Expand Up @@ -136,6 +137,9 @@ services:
context: ./gitea/
extra_hosts:
- 'host.docker.internal:host-gateway'
depends_on:
studio_db:
condition: service_healthy
healthcheck:
test: [ "CMD", "curl", "--fail", "http://localhost:3000" ]
interval: 30s
Expand Down Expand Up @@ -168,7 +172,6 @@ services:
depends_on:
studio_db:
condition: service_healthy

build:
context: backend
dockerfile: Migrations.Dockerfile
Expand All @@ -178,4 +181,3 @@ services:
- PGUSER=designer_admin
- PGPASSWORD=${POSTGRES_PASSWORD}
- PGDATABASE=designerdb

91 changes: 54 additions & 37 deletions development/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ const path = require('path');
const writeEnvFile = require('./utils/write-env-file.js');

const startingDockerCompose = () => runCommand('docker compose up -d --remove-orphans --build');
const restartComposeServices = () =>
runCommand('docker compose down && docker compose up -d --remove-orphans');
const buildAndStartComposeService = (service) =>
runCommand(`docker compose up -d ${service} --build`);
const stopComposeService = (service) => runCommand(`docker compose down ${service}`);

const createUser = (username, password, admin) =>
runCommand(
Expand All @@ -23,19 +24,9 @@ const createUser = (username, password, admin) =>
].join(' '),
);

const ensureUserPassword = (username, password) =>
runCommand(
[
`docker exec studio-repositories gitea admin user change-password`,
`--username ${username}`,
`--password ${password}`,
`--must-change-password=false`,
].join(' '),
);

const createTestDepOrg = (env) =>
giteaApi({
path: '/repos/api/v1/orgs',
path: '/api/v1/orgs',
method: 'POST',
user: env.GITEA_ADMIN_USER,
pass: env.GITEA_ADMIN_PASS,
Expand All @@ -49,7 +40,7 @@ const createTestDepTeams = async (env) => {
const allTeams = require(path.resolve(__dirname, 'data', 'gitea-teams.json'));

const existingTeams = await giteaApi({
path: `/repos/api/v1/orgs/${env.GITEA_ORG_USER}/teams`,
path: `/api/v1/orgs/${env.GITEA_ORG_USER}/teams`,
method: 'GET',
user: env.GITEA_ADMIN_USER,
pass: env.GITEA_ADMIN_PASS,
Expand All @@ -59,7 +50,7 @@ const createTestDepTeams = async (env) => {
const existing = existingTeams.find((t) => t.name === team.name);
if (!existing) {
await giteaApi({
path: `/repos/api/v1/orgs/${env.GITEA_ORG_USER}/teams`,
path: `/api/v1/orgs/${env.GITEA_ORG_USER}/teams`,
method: 'POST',
user: env.GITEA_ADMIN_USER,
pass: env.GITEA_ADMIN_PASS,
Expand All @@ -76,19 +67,19 @@ const createTestDepTeams = async (env) => {

const createOidcClientIfNotExists = async (env) => {
const clients = await giteaApi({
path: `/repos/api/v1/user/applications/oauth2`,
path: `/api/v1/user/applications/oauth2`,
method: 'GET',
user: env.GITEA_ADMIN_USER,
pass: env.GITEA_ADMIN_PASS,
});

const shouldCreateClient = !clients.some((app) => app.name === 'LocalTestOidcClient');
if (!shouldCreateClient) {
return;
return null;
}

var createdClient = await giteaApi({
path: `/repos/api/v1/user/applications/oauth2`,
path: `/api/v1/user/applications/oauth2`,
method: 'POST',
user: env.GITEA_ADMIN_USER,
pass: env.GITEA_ADMIN_PASS,
Expand All @@ -102,19 +93,17 @@ const createOidcClientIfNotExists = async (env) => {
env.CLIENT_ID = createdClient.client_id;
env.CLIENT_SECRET = createdClient.client_secret;

writeEnvFile(env);
// reload designer with new clientid and secret
restartComposeServices();
await waitFor('http://studio.localhost/repos/');
return env;
};

const addUserToSomeTestDepTeams = async (env) => {
const teams = await giteaApi({
path: `/repos/api/v1/orgs/${env.GITEA_ORG_USER}/teams`,
path: `/api/v1/orgs/${env.GITEA_ORG_USER}/teams`,
method: 'GET',
user: env.GITEA_ADMIN_USER,
pass: env.GITEA_ADMIN_PASS,
});

for (const teamName of [
'Owners',
'Deploy-TT02',
Expand All @@ -134,8 +123,9 @@ const addUserToSomeTestDepTeams = async (env) => {
'AccessLists-TT02',
]) {
const existing = teams.find((t) => t.name === teamName);

await giteaApi({
path: `/repos/api/v1/teams/${existing.id}/members/${env.GITEA_ADMIN_USER}`,
path: `/api/v1/teams/${existing.id}/members/${env.GITEA_ADMIN_USER}`,
method: 'PUT',
user: env.GITEA_ADMIN_USER,
pass: env.GITEA_ADMIN_PASS,
Expand All @@ -160,8 +150,9 @@ const addUserToSomeTestDepTeams = async (env) => {
'AccessLists-TT02',
]) {
const existing = teams.find((t) => t.name === teamName);

await giteaApi({
path: `/repos/api/v1/teams/${existing.id}/members/${env.GITEA_CYPRESS_USER}`,
path: `/api/v1/teams/${existing.id}/members/${env.GITEA_CYPRESS_USER}`,
method: 'PUT',
user: env.GITEA_ADMIN_USER,
pass: env.GITEA_ADMIN_PASS,
Expand All @@ -178,25 +169,51 @@ const addReleaseAndDeployTestDataToDb = async () =>
].join(' '),
);

const setupEnvironment = async (env) => {
buildAndStartComposeService('studio_db');
buildAndStartComposeService('studio_repositories');
await new Promise((resolve) =>
setTimeout(() => {
resolve(null);
}, 40000),
);

createUser(env.GITEA_ADMIN_USER, env.GITEA_ADMIN_PASS, true);
createUser(env.GITEA_CYPRESS_USER, env.GITEA_CYPRESS_PASS, false);
await createTestDepOrg(env);
await createTestDepTeams(env);
await addUserToSomeTestDepTeams(env);
const result = await createOidcClientIfNotExists(env);

await createCypressEnvFile(env);

stopComposeService('studio_db');
stopComposeService('studio_repositories');
return result;
};

const script = async () => {
const env = ensureDotEnv();
await dnsIsOk('studio.localhost');
if (!(env.IGNORE_DOCKER_DNS_LOOKUP === 'true')) {
await dnsIsOk('host.docker.internal');
}
await startingDockerCompose();
await waitFor('http://studio.localhost/repos/');
await createUser(env.GITEA_ADMIN_USER, env.GITEA_ADMIN_PASS, true);
await ensureUserPassword(env.GITEA_ADMIN_USER, env.GITEA_ADMIN_PASS);
await createUser(env.GITEA_CYPRESS_USER, env.GITEA_CYPRESS_PASS, false);
await ensureUserPassword(env.GITEA_CYPRESS_USER, env.GITEA_CYPRESS_PASS);
await createTestDepOrg(env);
await createTestDepTeams(env);
await addUserToSomeTestDepTeams(env);
await createOidcClientIfNotExists(env);
await createCypressEnvFile(env);

const result = await setupEnvironment(env);
if (result) {
writeEnvFile(result);
}

startingDockerCompose();
await waitFor('http://studio.localhost', 80);

await addReleaseAndDeployTestDataToDb();
process.exit(0);
};

script().then().catch(console.error);
script()
.then()
.catch((error) => {
console.error(error);
process.exit(1);
});
22 changes: 16 additions & 6 deletions development/utils/create-cypress-env-file.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module.exports = async (env) => {
const tokenPrefix = 'setup.js';

const allTokens = await giteaApi({
path: `/repos/api/v1/users/${env.GITEA_ADMIN_USER}/tokens`,
path: `/api/v1/users/${env.GITEA_ADMIN_USER}/tokens`,
method: 'GET',
user: env.GITEA_ADMIN_USER,
pass: env.GITEA_ADMIN_PASS,
Expand All @@ -17,22 +17,32 @@ module.exports = async (env) => {
if (token.name.startsWith(tokenPrefix)) {
deleteTokenOperations.push(
giteaApi({
path: `/repos/api/v1/users/${env.GITEA_ADMIN_USER}/tokens/${token.id}`,
path: `/api/v1/users/${env.GITEA_ADMIN_USER}/tokens/${token.id}`,
method: 'DELETE',
user: env.GITEA_ADMIN_USER,
pass: env.GITEA_ADMIN_PASS,
})
}),
);
}
});
const result = await giteaApi({
path: `/repos/api/v1/users/${env.GITEA_ADMIN_USER}/tokens`,
path: `/api/v1/users/${env.GITEA_ADMIN_USER}/tokens`,
method: 'POST',
user: env.GITEA_ADMIN_USER,
pass: env.GITEA_ADMIN_PASS,
body: {
name: tokenPrefix + ' ' + Date.now(),
scopes: ['write:activitypub', 'write:admin', 'write:issue', 'write:misc', 'write:notification', 'write:organization', 'write:package', 'write:repository', 'write:user'],
scopes: [
'write:activitypub',
'write:admin',
'write:issue',
'write:misc',
'write:notification',
'write:organization',
'write:package',
'write:repository',
'write:user',
],
},
});
const envFile = {
Expand All @@ -53,7 +63,7 @@ module.exports = async (env) => {
'frontend',
'testing',
'cypress',
'cypress.env.json'
'cypress.env.json',
);
fs.writeFileSync(cypressEnvFilePath, JSON.stringify(envFile, null, 2) + os.EOL, 'utf-8');
console.log('Wrote a new:', cypressEnvFilePath);
Expand Down
49 changes: 20 additions & 29 deletions development/utils/gitea-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,24 @@
* @param options
* @returns {Promise<unknown>}
*/
module.exports = (options) =>
new Promise(function (resolve, reject) {
const req = request(
{
host: 'studio.localhost',
path: options.path,
auth: [options.user, options.pass].join(':'),
method: options.method,
headers: {
'Content-Type': 'application/json',
},
module.exports = async (options) => {
try {
const response = await fetch(`${options.hostname || 'http://localhost:3000'}${options.path}`, {

Check warning

Code scanning / CodeQL

File data in outbound network request Medium

Outbound network request depends on
file data
.
method: options.method,
headers: {
'Content-Type': 'application/json',
Authorization: `Basic ${Buffer.from(`${options.user}:${options.pass}`).toString('base64')}`,
},

Check warning

Code scanning / CodeQL

File data in outbound network request Medium

Outbound network request depends on
file data
.
(response) => {
const data = [];
response.on('data', (chunk) => data.push(chunk));
response.on('end', () => {
console.log(options.method, options.path, response.statusCode, response.statusMessage);
if (data.length) {
resolve(JSON.parse(data.join()));
} else {
resolve();
}
});
}
);
if (options.body) {
req.write(JSON.stringify(options.body));
}
req.end(() => {});
});
body: options.body ? JSON.stringify(options.body) : undefined,

Check warning

Code scanning / CodeQL

File data in outbound network request Medium

Outbound network request depends on
file data
.
});

const data = await response.json().catch((err) => {
console.error('Error:', err);
});
console.log(options.method, options.path, response.status, response.statusText);
return data;
} catch (error) {
console.error('Error:', error);
throw error;
}
};
4 changes: 2 additions & 2 deletions development/utils/wait-for.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
const { get } = require('http');
module.exports = (url) =>
module.exports = (url, givenAttempts = 10) =>
new Promise(function (resolve, reject) {
let attempts = 0;

const checkAttempts = () => {
attempts++;
if (attempts > 10) {
if (attempts > givenAttempts) {
clearInterval(intervalId);
console.log('Giving up: ', url);
reject('Giving up this');
Expand Down
1 change: 1 addition & 0 deletions development/utils/write-env-file.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ module.exports = (envData) => {
const fd = fs.openSync(dotenvLocations, O_RDWR | O_CREAT, 0o600);
Object.keys(envData).forEach((key) => newEnv.push([key, envData[key]].join('=')));
fs.writeFileSync(fd, newEnv.join(os.EOL), 'utf-8');
fs.closeSync(fd);
console.log('Ensuring .env variables at:', dotenvLocations);
};
2 changes: 2 additions & 0 deletions frontend/testing/playwright/scripts/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const environment: Record<string, string> = {
const createGiteaAccessToken = async (): Promise<void> => {
const result = await giteaApi({
path: `/repos/api/v1/users/${process.env.GITEA_CYPRESS_USER}/tokens`,
hostname: 'http://studio.localhost',
method: 'POST',
user: process.env.GITEA_CYPRESS_USER,
pass: process.env.GITEA_CYPRESS_PASS,
Expand All @@ -37,6 +38,7 @@ const createGiteaAccessToken = async (): Promise<void> => {
],
},
});
console.log('result------', result);
environment.GITEA_ACCESS_TOKEN = result.sha1;
};

Expand Down