Skip to content

Commit 0b20a04

Browse files
authored
Merge pull request #11398 from Turbo87/trustpub-ui
Implement Trusted Publishing user interface
2 parents 46b5393 + b00a70f commit 0b20a04

File tree

18 files changed

+1231
-11
lines changed

18 files changed

+1231
-11
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import ApplicationAdapter from './application';
2+
3+
export default class TrustpubGitHubConfigAdapter extends ApplicationAdapter {
4+
pathForType() {
5+
return 'trusted_publishing/github_configs';
6+
}
7+
}

app/controllers/crate/settings.js renamed to app/controllers/crate/settings/index.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { task } from 'ember-concurrency';
77

88
export default class CrateSettingsController extends Controller {
99
@service notifications;
10+
@service store;
1011

1112
crate = null;
1213
username = '';
@@ -64,6 +65,22 @@ export default class CrateSettingsController extends Controller {
6465
this.notifications.error(message);
6566
}
6667
});
68+
69+
removeConfigTask = task(async config => {
70+
try {
71+
await config.destroyRecord();
72+
this.notifications.success('Trusted Publishing configuration removed successfully');
73+
} catch (error) {
74+
let message = 'Failed to remove Trusted Publishing configuration';
75+
76+
let detail = error.errors?.[0]?.detail;
77+
if (detail && !detail.startsWith('{')) {
78+
message += `: ${detail}`;
79+
}
80+
81+
this.notifications.error(message);
82+
}
83+
});
6784
}
6885

6986
function removeOwner(owners, target) {
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import Controller from '@ember/controller';
2+
import { action } from '@ember/object';
3+
import { service } from '@ember/service';
4+
import { tracked } from '@glimmer/tracking';
5+
6+
import { task } from 'ember-concurrency';
7+
8+
export default class NewTrustedPublisherController extends Controller {
9+
@service notifications;
10+
@service store;
11+
@service router;
12+
13+
@tracked publisher = 'GitHub';
14+
@tracked repositoryOwner = '';
15+
@tracked repositoryName = '';
16+
@tracked workflowFilename = '';
17+
@tracked environment = '';
18+
@tracked repositoryOwnerInvalid = false;
19+
@tracked repositoryNameInvalid = false;
20+
@tracked workflowFilenameInvalid = false;
21+
22+
get crate() {
23+
return this.model.crate;
24+
}
25+
26+
get publishers() {
27+
return ['GitHub'];
28+
}
29+
30+
saveConfigTask = task(async () => {
31+
if (!this.validate()) return;
32+
33+
let config = this.store.createRecord('trustpub-github-config', {
34+
crate: this.crate,
35+
repository_owner: this.repositoryOwner,
36+
repository_name: this.repositoryName,
37+
workflow_filename: this.workflowFilename,
38+
environment: this.environment || null,
39+
});
40+
41+
try {
42+
// Save the new config on the backend
43+
await config.save();
44+
45+
this.repositoryOwner = '';
46+
this.repositoryName = '';
47+
this.workflowFilename = '';
48+
this.environment = '';
49+
50+
// Navigate back to the crate settings page
51+
this.notifications.success('Trusted Publishing configuration added successfully');
52+
this.router.transitionTo('crate.settings', this.crate.id);
53+
} catch (error) {
54+
// Notify the user
55+
let message = 'An error has occurred while adding the Trusted Publishing configuration';
56+
57+
let detail = error.errors?.[0]?.detail;
58+
if (detail && !detail.startsWith('{')) {
59+
message += `: ${detail}`;
60+
}
61+
62+
this.notifications.error(message);
63+
}
64+
});
65+
66+
validate() {
67+
this.repositoryOwnerInvalid = !this.repositoryOwner;
68+
this.repositoryNameInvalid = !this.repositoryName;
69+
this.workflowFilenameInvalid = !this.workflowFilename;
70+
71+
return !this.repositoryOwnerInvalid && !this.repositoryNameInvalid && !this.workflowFilenameInvalid;
72+
}
73+
74+
@action resetRepositoryOwnerValidation() {
75+
this.repositoryOwnerInvalid = false;
76+
}
77+
78+
@action resetRepositoryNameValidation() {
79+
this.repositoryNameInvalid = false;
80+
}
81+
82+
@action resetWorkflowFilenameValidation() {
83+
this.workflowFilenameInvalid = false;
84+
}
85+
}

app/models/trustpub-github-config.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import Model, { attr, belongsTo } from '@ember-data/model';
2+
3+
export default class TrustpubGitHubConfig extends Model {
4+
@belongsTo('crate', { async: true, inverse: null }) crate;
5+
@attr repository_owner;
6+
@attr repository_name;
7+
@attr workflow_filename;
8+
@attr environment;
9+
@attr('date') created_at;
10+
}

app/router.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ Router.map(function () {
1919
this.route('reverse-dependencies', { path: 'reverse_dependencies' });
2020

2121
this.route('owners');
22-
this.route('settings');
22+
this.route('settings', function () {
23+
this.route('new-trusted-publisher');
24+
});
2325
this.route('delete');
2426

2527
// Well-known routes

app/routes/crate/settings.js

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ export default class SettingsRoute extends AuthenticatedRoute {
66
@service router;
77
@service session;
88

9-
async afterModel(crate, transition) {
9+
async beforeModel(transition) {
10+
await super.beforeModel(...arguments);
11+
1012
let user = this.session.currentUser;
11-
let owners = await crate.owner_user;
13+
let owners = await this.modelFor('crate').owner_user;
1214
let isOwner = owners.some(owner => owner.id === user.id);
1315
if (!isOwner) {
1416
this.router.replaceWith('catch-all', {
@@ -17,10 +19,4 @@ export default class SettingsRoute extends AuthenticatedRoute {
1719
});
1820
}
1921
}
20-
21-
setupController(controller) {
22-
super.setupController(...arguments);
23-
let crate = this.modelFor('crate');
24-
controller.set('crate', crate);
25-
}
2622
}

app/routes/crate/settings/index.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import Route from '@ember/routing/route';
2+
import { service } from '@ember/service';
3+
4+
export default class SettingsIndexRoute extends Route {
5+
@service store;
6+
7+
async model() {
8+
let crate = this.modelFor('crate');
9+
10+
let githubConfigs = await this.store.query('trustpub-github-config', { crate: crate.name });
11+
12+
return { crate, githubConfigs };
13+
}
14+
15+
setupController(controller, { crate, githubConfigs }) {
16+
super.setupController(...arguments);
17+
18+
controller.set('crate', crate);
19+
controller.set('githubConfigs', githubConfigs);
20+
}
21+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import Route from '@ember/routing/route';
2+
3+
export default class NewTrustedPublisherRoute extends Route {
4+
async model() {
5+
let crate = this.modelFor('crate');
6+
return { crate };
7+
}
8+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import ApplicationSerializer from './application';
2+
3+
export default class TrustpubGitHubConfigSerializer extends ApplicationSerializer {
4+
modelNameFromPayloadKey() {
5+
return 'trustpub-github-config';
6+
}
7+
8+
payloadKeyFromModelName() {
9+
return 'github_config';
10+
}
11+
}

app/styles/crate/settings.module.css renamed to app/styles/crate/settings/index.module.css

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.owners-header {
1+
.owners-header, .trusted-publishing-header {
22
display: flex;
33
justify-content: space-between;
44
align-items: center;
@@ -43,6 +43,30 @@
4343
}
4444
}
4545

46+
.trustpub {
47+
background-color: light-dark(white, #141413);
48+
border-radius: var(--space-3xs);
49+
box-shadow: 0 1px 3px light-dark(hsla(51, 90%, 42%, .35), #232321);
50+
51+
tbody > tr > td {
52+
border-top: 1px solid light-dark(hsla(51, 90%, 42%, .25), #232321);
53+
}
54+
55+
th, td {
56+
text-align: left;
57+
padding: var(--space-s) var(--space-m);
58+
}
59+
60+
.details {
61+
font-size: 0.85em;
62+
line-height: 1.5;
63+
}
64+
65+
.actions {
66+
text-align: right;
67+
}
68+
}
69+
4670
.email-column {
4771
width: 25%;
4872
color: var(--main-color-light);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
.form-group, .buttons {
2+
margin: var(--space-m) 0;
3+
}
4+
5+
.publisher-select {
6+
max-width: 440px;
7+
width: 100%;
8+
padding-right: var(--space-m);
9+
background-image: url("/assets/dropdown.svg");
10+
background-repeat: no-repeat;
11+
background-position: calc(100% - var(--space-2xs)) center;
12+
background-size: 10px;
13+
appearance: none;
14+
}
15+
16+
.note {
17+
margin-top: var(--space-2xs);
18+
font-size: 0.85em;
19+
}
20+
21+
.input {
22+
max-width: 440px;
23+
width: 100%;
24+
}
25+
26+
.buttons {
27+
display: flex;
28+
gap: var(--space-2xs);
29+
flex-wrap: wrap;
30+
}
31+
32+
.add-button {
33+
border-radius: 4px;
34+
35+
.spinner {
36+
margin-left: var(--space-2xs);
37+
}
38+
}
39+
40+
.cancel-button {
41+
border-radius: 4px;
42+
}

app/templates/crate/settings.hbs renamed to app/templates/crate/settings/index.hbs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,47 @@
5454
{{/each}}
5555
</div>
5656

57+
{{! The "Trusted Publishing" section is hidden for now until we make this feature publicly available. }}
58+
{{#if this.githubConfigs}}
59+
<div local-class="trusted-publishing-header">
60+
<h2>Trusted Publishing</h2>
61+
<LinkTo @route="crate.settings.new-trusted-publisher" class="button button--small" data-test-add-trusted-publisher-button>
62+
Add
63+
</LinkTo>
64+
</div>
65+
66+
<table local-class="trustpub" data-test-trusted-publishing>
67+
<thead>
68+
<tr>
69+
<th>Publisher</th>
70+
<th>Details</th>
71+
<th></th>
72+
</tr>
73+
</thead>
74+
<tbody>
75+
{{#each this.githubConfigs as |config|}}
76+
<tr data-test-github-config={{config.id}}>
77+
<td>GitHub</td>
78+
<td local-class="details">
79+
<strong>Repository:</strong> {{config.repository_owner}}/{{config.repository_name}}<br>
80+
<strong>Workflow:</strong> {{config.workflow_filename}}<br>
81+
{{#if config.environment}}
82+
<strong>Environment:</strong> {{config.environment}}
83+
{{/if}}
84+
</td>
85+
<td local-class="actions">
86+
<button type="button" class="button button--small" data-test-remove-config-button {{on "click" (perform this.removeConfigTask config)}}>Remove</button>
87+
</td>
88+
</tr>
89+
{{else}}
90+
<tr data-test-no-config>
91+
<td colspan="3">No trusted publishers configured for this crate.</td>
92+
</tr>
93+
{{/each}}
94+
</tbody>
95+
</table>
96+
{{/if}}
97+
5798
<h2>Danger Zone</h2>
5899

59100
<div>

0 commit comments

Comments
 (0)